Compare commits

..

689 Commits

Author SHA1 Message Date
Frappe PR Bot
e27016339f chore(release): Bumped to Version 15.112.0
# [15.112.0](https://github.com/frappe/erpnext/compare/v15.111.0...v15.112.0) (2026-06-16)

### Bug Fixes

* **accounts:** removed whitelist on `get_balance_on` (backport [#55956](https://github.com/frappe/erpnext/issues/55956)) ([#55964](https://github.com/frappe/erpnext/issues/55964)) ([64bbc01](64bbc019ad))
* Add authorization checks on internal functions (backport [#55709](https://github.com/frappe/erpnext/issues/55709)) (backport [#55726](https://github.com/frappe/erpnext/issues/55726)) ([#55736](https://github.com/frappe/erpnext/issues/55736)) ([8c7a313](8c7a313a38))
* added doctype filter validation for sales person wise transaction summary report (backport [#55812](https://github.com/frappe/erpnext/issues/55812)) ([#55817](https://github.com/frappe/erpnext/issues/55817)) ([f1fc9e3](f1fc9e3261))
* apply user permissions to receivable/payable reports ([c03a66a](c03a66a1bf))
* BOM Creator by removing redundant permissions ([08d9b82](08d9b8275d))
* **bom:** fetch routing operations when Routing is selected ([#55813](https://github.com/frappe/erpnext/issues/55813)) ([02e38e8](02e38e80a7))
* **budget:** add root_type filter on account field ([#55934](https://github.com/frappe/erpnext/issues/55934)) ([e1fbf78](e1fbf78409))
* clearance date update with permission checks ([46b3e0c](46b3e0c385))
* **company:** replaced "this company" with company name on delete transactions dialog (backport [#56021](https://github.com/frappe/erpnext/issues/56021)) ([#56022](https://github.com/frappe/erpnext/issues/56022)) ([8a9461f](8a9461ff45))
* converted whitelist non class methods to class methods ([7c78aa6](7c78aa6e5d))
* **get_exchange_rate:** using get_single_value to fetch `disabled` value from `currency_exchange_settings` ([9405b49](9405b49e93))
* **Lead:** stop storing Gravatar image URLs for Leads (backport [#55880](https://github.com/frappe/erpnext/issues/55880)) ([#55881](https://github.com/frappe/erpnext/issues/55881)) ([8b3a0fe](8b3a0fe045))
* **manufacturing:** fetch raw materials from work order ([7e9c1ef](7e9c1efab7))
* multiple issues related to BOM Creator ([f4e6f14](f4e6f14342))
* opportunity creation from contact us page (backport [#55841](https://github.com/frappe/erpnext/issues/55841)) ([#55866](https://github.com/frappe/erpnext/issues/55866)) ([d460577](d4605771da))
* pass source cost center to target cost center ([209977f](209977f6a3))
* pemission for whitelist functions ([559c95c](559c95c8a8))
* permission in bom compare tool ([ef3046d](ef3046dca2))
* permissions in workstation file ([808ca06](808ca06801))
* prefetch batchwise valuations before streaming SLEs in stock ageing ([c617650](c6176500d2))
* prevent exchange rate flow from transaction to payment ([72c7b79](72c7b79933))
* provision to recalculate valuation rate during reposting ([df0c8ee](df0c8ee21e))
* recalculate incoming rate in SLE for purchase documents during repost ([081887b](081887bec5))
* regression issues related to security fixes (backport [#55902](https://github.com/frappe/erpnext/issues/55902)) ([#55925](https://github.com/frappe/erpnext/issues/55925)) ([1f47b24](1f47b2417b))
* restricting currency_exchange_settings write permission only to system manager ([7b65206](7b6520664c))
* show company name in delete transactions confirmation dialog ([3356583](3356583865))
* show user disable audit log ([559585f](559585fb7b))
* **stock:** enable quality inspection for all Stock Entry purposes ([1ffef19](1ffef19957)), closes [#55830](https://github.com/frappe/erpnext/issues/55830) [#70945](https://github.com/frappe/erpnext/issues/70945)
* **stock:** make uom mandatory in item uom table ([c3a3eb3](c3a3eb3df3))
* **stock:** show only batched items in batch item selector ([7f30a2c](7f30a2cfb6))
* **stock:** update stock value calculation in stock balance report ([18ad323](18ad323828))
* sync employee user status after save ([e40999c](e40999c879))
* update system manager permissions ([81e05fc](81e05fc1f6))
* updated role based permission for terms and conditions doctype (backport [#55674](https://github.com/frappe/erpnext/issues/55674)) ([#55675](https://github.com/frappe/erpnext/issues/55675)) ([5f5b2a6](5f5b2a6ae2))
* use frankfurter v2 by default for new install ([d62985d](d62985d9a7))

### Features

* **currency exchange settings:** frankfurter v2 support ([70628c0](70628c06c9))
2026-06-16 21:53:42 +00:00
Diptanil Saha
4cf507fd68 Merge pull request #55980 from frappe/version-15-hotfix
chore: release v15
2026-06-17 03:21:59 +05:30
mergify[bot]
8c7a313a38 fix: Add authorization checks on internal functions (backport #55709) (backport #55726) (#55736)
Co-authored-by: Ankush Menat <ankush@frappe.io>
fix: Add authorization checks on internal functions (backport #55709) (#55726)
fix: Add authorization checks on internal functions (#55709)
2026-06-16 21:34:10 +00:00
mergify[bot]
5f5b2a6ae2 fix: updated role based permission for terms and conditions doctype (backport #55674) (#55675)
* fix: updated role based permission for terms and conditions doctype (#55674)

(cherry picked from commit 0ba2961103)

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

* chore: resolve conflicts

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2026-06-16 21:30:22 +00:00
mergify[bot]
8a9461ff45 fix(company): replaced "this company" with company name on delete transactions dialog (backport #56021) (#56022)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
fix(company): replaced "this company" with company name on delete transactions dialog (#56021)
2026-06-17 02:14:05 +05:30
mergify[bot]
64bbc019ad fix(accounts): removed whitelist on get_balance_on (backport #55956) (#55964)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
fix(accounts): removed whitelist on `get_balance_on` (#55956)
2026-06-16 22:46:15 +05:30
Mihir Kandoi
797e740aea Merge pull request #56001 from aerele/fix/qi-stock-entry-inspection-v15
fix(stock): enable quality inspection for all Stock Entry purposes
2026-06-16 21:27:22 +05:30
Mihir Kandoi
8f2bd2b77e Merge pull request #55991 from frappe/mergify/bp/version-15-hotfix/pr-55985
fix(stock): show only batched items in batch item selector (backport #55985)
2026-06-16 21:27:06 +05:30
Mihir Kandoi
776031b277 Merge pull request #56004 from frappe/mergify/bp/version-15-hotfix/pr-55978
fix: update system manager permissions (backport #55978)
2026-06-16 21:20:14 +05:30
nareshkannasln
81e05fc1f6 fix: update system manager permissions
(cherry picked from commit 501c8087cb)
2026-06-16 20:42:53 +05:30
Mihir Kandoi
ebdb23bdda chore: resolve conflicts 2026-06-16 20:37:19 +05:30
Mihir Kandoi
75a8b9097c Merge pull request #55553 from aerele/fix/support-#69882
fix(manufacturing): fetch raw materials from work order
2026-06-16 20:35:15 +05:30
Sudharsanan11
1ffef19957 fix(stock): enable quality inspection for all Stock Entry purposes
Backport of #55830 to version-15.

Allow creating a Quality Inspection from Stock Entries of any purpose
(not just Manufacture). check_item_quality_inspection now returns the
items for Stock Entry, and the inspection type is resolved as "Incoming"
for Manufacture / Material Receipt and "Outgoing" otherwise.

Ref: #70945
2026-06-16 20:26:21 +05:30
rohitwaghchaure
8a59385b22 Merge pull request #55995 from frappe/mergify/bp/version-15-hotfix/pr-55986
chore: removed redundant validation in SCR (backport #55986)
2026-06-16 19:40:59 +05:30
rohitwaghchaure
b15b2e1b2a Merge pull request #55990 from rohitwaghchaure/fixed-recalculate-valuation-rate-in-pr-v15
fix: provision to recalculate valuation rate during reposting
2026-06-16 19:38:50 +05:30
Rohit Waghchaure
df0c8ee21e fix: provision to recalculate valuation rate during reposting 2026-06-16 19:09:07 +05:30
Rohit Waghchaure
71c7045b98 chore: removed redundant validation in SCR
(cherry picked from commit 1b4487450c)
2026-06-16 13:38:27 +00:00
Dharanidharan2813
7f30a2cfb6 fix(stock): show only batched items in batch item selector
(cherry picked from commit 62f6d18143)

# Conflicts:
#	erpnext/stock/doctype/batch/batch.json
2026-06-16 13:00:51 +00:00
Sudharsanan Ashok
ab8b74e3d8 Merge pull request #55975 from frappe/mergify/bp/version-15-hotfix/pr-55943
refactor: ignore cancelled GLE's while looking for account (backport #55943)
2026-06-16 16:59:18 +05:30
Sudharsanan11
b22096a640 test(manufacturing): add test to validate the transferred raw materials from work order 2026-06-16 16:55:31 +05:30
ervishnucs
9546ab72e0 refactor: ignore cancelled GLE's while looking for account
(cherry picked from commit 40942401df)
2026-06-16 11:08:16 +00:00
Diptanil Saha
8f23f1f180 Revert "fix(budget): add root_type filter on account field (backport #55934)" (#55966) 2026-06-16 15:30:51 +05:30
ruthra kumar
a3a945a12f Merge pull request #55961 from frappe/mergify/bp/version-15-hotfix/pr-53152
fix: prevent exchange rate flow from transaction to payment (backport #53152)
2026-06-16 14:38:29 +05:30
ruthra kumar
48bcfc78ea Merge pull request #55954 from frappe/mergify/bp/version-15-hotfix/pr-55934
fix(budget): add root_type filter on account field (backport #55934)
2026-06-16 14:10:04 +05:30
ervishnucs
df823432d1 test: resolve failed testcases for exchage rate
(cherry picked from commit 3b5a203d61)
2026-06-16 08:33:20 +00:00
ervishnucs
72c7b79933 fix: prevent exchange rate flow from transaction to payment
(cherry picked from commit 934abe5c6d)
2026-06-16 08:33:19 +00:00
Shllokkk
e1fbf78409 fix(budget): add root_type filter on account field (#55934)
(cherry picked from commit 52b406f5f1)

# Conflicts:
#	erpnext/accounts/doctype/budget/budget.js
2026-06-16 06:17:24 +00:00
Sudharsanan Ashok
2c2adffacf Merge pull request #55939 from frappe/mergify/bp/version-15-hotfix/pr-55928
fix(stock): update stock value calculation in stock balance report (backport #55928)
2026-06-15 19:27:28 +05:30
Sudharsanan11
18ad323828 fix(stock): update stock value calculation in stock balance report
(cherry picked from commit 2492dfa558)
2026-06-15 12:40:51 +00:00
rohitwaghchaure
4c6667ec30 Merge pull request #55932 from frappe/mergify/bp/version-15-hotfix/pr-55926
fix: recalculate incoming rate in SLE for purchase documents during repost (backport #55926)
2026-06-15 17:52:50 +05:30
Rohit Waghchaure
081887bec5 fix: recalculate incoming rate in SLE for purchase documents during repost
(cherry picked from commit 867ee484b9)
2026-06-15 11:34:14 +00:00
mergify[bot]
1f47b2417b fix: regression issues related to security fixes (backport #55902) (#55925)
* fix: regression issues related to security fixes

(cherry picked from commit be1aa0e5eb)

# Conflicts:
#	erpnext/controllers/subcontracting_inward_controller.py
#	erpnext/selling/doctype/sales_order/sales_order.py
#	erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.py

* refactor: consolidate duplicate get_party_bank_account into bank_account.py

(cherry picked from commit ede13cb3bd)

* chore: fix conflicts

Removed duplicate import of get_party_bank_account and consolidated imports.

* chore: fix conflicts

Removed the update_subcontracting_order_status method to address regression issues related to security fixes.

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2026-06-15 11:23:08 +00:00
Diptanil Saha
b4be0834f2 Merge pull request #55918 from frappe/mergify/bp/version-15-hotfix/pr-55755
feat(currency exchange settings): frankfurter v2 support (backport #55755)
2026-06-15 15:23:53 +05:30
Diptanil Saha
80741ceb67 chore: resolve conflicts 2026-06-15 14:39:29 +05:30
diptanilsaha
9405b49e93 fix(get_exchange_rate): using get_single_value to fetch disabled value from currency_exchange_settings
(cherry picked from commit abb579e2db)
2026-06-15 08:53:17 +00:00
diptanilsaha
7b6520664c fix: restricting currency_exchange_settings write permission only to system manager
(cherry picked from commit 0c2d5488a6)

# Conflicts:
#	erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
2026-06-15 08:53:17 +00:00
diptanilsaha
64fc3ac309 test: fixed currency exchange test for frankfurter v2 api
(cherry picked from commit 138f683a68)
2026-06-15 08:53:16 +00:00
diptanilsaha
d62985d9a7 fix: use frankfurter v2 by default for new install
(cherry picked from commit 479f9f63c9)
2026-06-15 08:53:16 +00:00
diptanilsaha
70628c06c9 feat(currency exchange settings): frankfurter v2 support
(cherry picked from commit 56bfe6b6a6)

# Conflicts:
#	erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
2026-06-15 08:53:16 +00:00
Mihir Kandoi
75d40651d1 Merge pull request #55907 from frappe/mergify/bp/version-15-hotfix/pr-55906
fix: show company name in delete transactions confirmation dialog (backport #55906)
2026-06-15 11:00:32 +05:30
Raghav Ruia
3356583865 fix: show company name in delete transactions confirmation dialog
Display the actual company name in bold within the confirmation dialog
label so users immediately know which company they must type to confirm,
reducing the risk of accidental data loss.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 87d26a2d67)
2026-06-15 04:20:25 +00:00
Sudharsanan11
7e9c1efab7 fix(manufacturing): fetch raw materials from work order 2026-06-15 01:03:00 +05:30
mergify[bot]
fe3f44f643 ci: set disabledLabels and context for greptile (backport #55883) (#55884)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2026-06-13 19:03:30 +00:00
mergify[bot]
8b3a0fe045 fix(Lead): stop storing Gravatar image URLs for Leads (backport #55880) (#55881)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Lead): stop storing Gravatar image URLs for Leads (#55880)
2026-06-13 18:19:13 +00:00
Mihir Kandoi
3d6a4eebee Merge pull request #55864 from frappe/mergify/bp/version-15-hotfix/pr-55853
fix: pass source cost center to target cost center (backport #55853)
2026-06-13 23:43:10 +05:30
rohitwaghchaure
0a3c53b16d Merge pull request #55875 from frappe/mergify/bp/version-15-hotfix/pr-55874
fix: permission in bom compare tool (backport #55874)
2026-06-13 19:40:36 +05:30
Rohit Waghchaure
ef3046dca2 fix: permission in bom compare tool
(cherry picked from commit e6fdb3702a)
2026-06-13 13:41:00 +00:00
rohitwaghchaure
5addc66301 Merge pull request #55873 from frappe/mergify/bp/version-15-hotfix/pr-55872
fix: pemission for whitelist functions (backport #55872)
2026-06-13 19:03:18 +05:30
rohitwaghchaure
08d9b8275d fix: BOM Creator by removing redundant permissions
Removed unnecessary permission checks for whitelist functions and cleaned up the code.
2026-06-13 18:46:40 +05:30
rohitwaghchaure
46b3e0c385 fix: clearance date update with permission checks
Refactor update_clearance_date to check permissions for payment documents before processing entries.
2026-06-13 18:43:12 +05:30
Rohit Waghchaure
559c95c8a8 fix: pemission for whitelist functions
(cherry picked from commit dd56e80512)

# Conflicts:
#	erpnext/accounts/doctype/bank_clearance/bank_clearance.py
#	erpnext/manufacturing/doctype/bom_creator/bom_creator.py
#	erpnext/subcontracting/doctype/subcontracting_inward_order/subcontracting_inward_order.py
2026-06-13 12:45:31 +00:00
mergify[bot]
d4605771da fix: opportunity creation from contact us page (backport #55841) (#55866)
* fix: opportunity creation from contact us page (#55841)

(cherry picked from commit c933e34914)

# Conflicts:
#	erpnext/crm/doctype/crm_settings/crm_settings.json
#	erpnext/templates/utils.py

* chore: resolve conflict (crm_settings.json)

* chore: resolve conflicts (send_message)

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2026-06-13 09:08:30 +00:00
SandraFrappe
209977f6a3 fix: pass source cost center to target cost center
(cherry picked from commit 9ea766fc10)
2026-06-12 15:27:54 +00:00
rohitwaghchaure
eec11ac7b2 Merge pull request #55828 from rohitwaghchaure/fixed-bom-creator-security-issues-v15
fix: multiple issues related to BOM Creator
2026-06-12 19:52:19 +05:30
Rohit Waghchaure
7c78aa6e5d fix: converted whitelist non class methods to class methods 2026-06-12 19:37:41 +05:30
Rohit Waghchaure
f4e6f14342 fix: multiple issues related to BOM Creator 2026-06-12 19:01:56 +05:30
rohitwaghchaure
48886467ec Merge pull request #55861 from frappe/mergify/bp/version-15-hotfix/pr-55858
fix: permissions in workstation file (backport #55844) (backport #55858)
2026-06-12 19:00:06 +05:30
rohitwaghchaure
baafb95e74 chore: fix conflicts 2026-06-12 18:36:53 +05:30
rohitwaghchaure
f4630273ad chore: fix linters issue
(cherry picked from commit 11c7a35eae)
2026-06-12 12:29:08 +00:00
rohitwaghchaure
a65629da1a chore: fix conflicts
(cherry picked from commit d04965b6b2)
2026-06-12 12:29:08 +00:00
Rohit Waghchaure
808ca06801 fix: permissions in workstation file
(cherry picked from commit cf127e8900)

# Conflicts:
#	erpnext/manufacturing/doctype/workstation/workstation.py
(cherry picked from commit 0fea93388d)

# Conflicts:
#	erpnext/manufacturing/doctype/workstation/workstation.py
2026-06-12 12:29:08 +00:00
Mihir Kandoi
74da3b8775 Merge pull request #55839 from frappe/mergify/bp/version-15-hotfix/pr-55838
fix(stock): make uom mandatory in item uom table (backport #55838)
2026-06-12 07:34:20 +05:30
pandiyan
c3a3eb3df3 fix(stock): make uom mandatory in item uom table
(cherry picked from commit a0177fdbe8)
2026-06-12 07:17:08 +05:30
Mihir Kandoi
d4baf0aeba Merge pull request #55833 from frappe/mergify/bp/version-15-hotfix/pr-55820
fix: show user disable audit log (backport #55820)
2026-06-11 21:50:07 +05:30
Mihir Kandoi
e40999c879 fix: sync employee user status after save 2026-06-11 20:58:35 +05:30
pandiyan
559585fb7b fix: show user disable audit log
(cherry picked from commit 73d1852773)
2026-06-11 15:01:32 +00:00
Mihir Kandoi
ee3f502538 Merge pull request #55814 from frappe/mergify/bp/version-15-hotfix/pr-55813
fix(bom): fetch routing operations when Routing is selected (backport #55813)
2026-06-11 20:30:35 +05:30
Mihir Kandoi
f37727d399 chore: resolve conflicts 2026-06-11 20:28:55 +05:30
Mihir Kandoi
b8d507e496 chore: resolve conflicts 2026-06-11 20:28:31 +05:30
ruthra kumar
11359b0ac2 Merge pull request #55819 from frappe/mergify/bp/version-15-hotfix/pr-55696
fix: apply user permissions to receivable/payable reports (backport #55696)
2026-06-11 16:59:48 +05:30
Kaushal Shriwas
7639a3360e test: clean up receivable entries to avoid cross-report leakage 2026-06-11 16:43:05 +05:30
mergify[bot]
f1fc9e3261 fix: added doctype filter validation for sales person wise transaction summary report (backport #55812) (#55817)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
fix: added doctype filter validation for sales person wise transaction summary report (#55812)
2026-06-11 07:08:59 +00:00
Kaushal Shriwas
b9a694bb37 test: cover user permission scoping in receivable report
(cherry picked from commit 4200d17c9b)
2026-06-11 06:58:06 +00:00
Kaushal Shriwas
c03a66a1bf fix: apply user permissions to receivable/payable reports
(cherry picked from commit b05abbc53b)
2026-06-11 06:58:06 +00:00
Mohammad Umair Sayed
02e38e80a7 fix(bom): fetch routing operations when Routing is selected (#55813)
fix(bom): fetch routing operations when routing is selected

frm.doc.operations is always an array in Frappe, so !frm.doc.operations
was always false (empty array [] is truthy in JS), causing get_routing()
to never fire when a Routing is selected on a BOM with no existing
operations.

Changed the guard to !frm.doc.operations.length so the fetch triggers
correctly when the operations table is empty.

Also wired the same fetch into the with_operations handler so that
enabling the checkbox after a Routing is already set will populate
operations without requiring the user to re-select the Routing.

Co-authored-by: Umair Sayed <umairsayed@Umairs-MacBook-Air-2.local>
(cherry picked from commit 9249fa89aa)

# Conflicts:
#	erpnext/manufacturing/doctype/bom/bom.js
2026-06-11 06:25:34 +00:00
Nabin Hait
2905669af4 Merge pull request #55794 from frappe/mergify/bp/version-15-hotfix/pr-55789
fix: prefetch batchwise valuations before streaming SLEs in stock ageing (backport #55789)
2026-06-10 12:14:37 +05:30
Mihir Kandoi
c6176500d2 fix: prefetch batchwise valuations before streaming SLEs in stock ageing
Stock Ageing iterates stock ledger entries through an unbuffered
(streaming) cursor. _get_batchwise_valuation() lazily queried
Batch.use_batchwise_valuation from inside that loop whenever a row
carried the legacy batch_no field, and the nested query invalidated
the active streaming result set — crashing the report (or silently
dropping the remaining rows, depending on the driver version).

Resolve the valuation flags in a single query before entering the
unbuffered cursor block; the lazy lookup now only serves callers that
pass stock ledger entries in directly, where no streaming is active.

Fixes https://github.com/frappe/erpnext/issues/55786

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 060a5c4eeb)
2026-06-10 06:07:48 +00:00
Frappe PR Bot
2b3e3dfd83 chore(release): Bumped to Version 15.111.0
# [15.111.0](https://github.com/frappe/erpnext/compare/v15.110.0...v15.111.0) (2026-06-10)

### Bug Fixes

* **accounts:** include asset items in purchase receipt validation ([#55150](https://github.com/frappe/erpnext/issues/55150)) ([611849f](611849f953))
* add permission checks in accounts whitelisted methods ([2d1c0dc](2d1c0dcb53))
* bypass project permission check when updating consumed material … (backport [#55645](https://github.com/frappe/erpnext/issues/55645)) ([#55706](https://github.com/frappe/erpnext/issues/55706)) ([cedd0a1](cedd0a1903))
* **cheque_print_template:** print format creation from cheque print template requires system manager (backport [#55708](https://github.com/frappe/erpnext/issues/55708)) ([#55711](https://github.com/frappe/erpnext/issues/55711)) ([2f5b93e](2f5b93e308))
* correct field order in Address and Contacts report ([bde46cf](bde46cffd0))
* correct field order in Address and Contacts report ([08f3cf9](08f3cf98f9))
* do not allow to make changes in SABB after submit ([07b6111](07b61113af))
* drop ignore_permissions handling from add_ac ([2d0e3fd](2d0e3fd9af))
* duplicating a Customer/Supplier shouldn't inherit the source's primary contact and address (backport [#55421](https://github.com/frappe/erpnext/issues/55421)) ([#55608](https://github.com/frappe/erpnext/issues/55608)) ([013bd1a](013bd1a566))
* handle multi-select stock ageing filters ([#55776](https://github.com/frappe/erpnext/issues/55776)) ([95f46df](95f46dfc01))
* **item:** format integer numeric variant attributes without decimals (backport [#55561](https://github.com/frappe/erpnext/issues/55561)) ([#55563](https://github.com/frappe/erpnext/issues/55563)) ([4772799](4772799db2))
* naming series issue ([e7eaa87](e7eaa87a77))
* persist main item code for MR plan items ([#55623](https://github.com/frappe/erpnext/issues/55623)) ([e8e0514](e8e0514a30))
* prevent leakage of party-derived fields in cross doctype transactions (backport [#55336](https://github.com/frappe/erpnext/issues/55336)) ([#55578](https://github.com/frappe/erpnext/issues/55578)) ([0e64acb](0e64acb0fa))
* prevent selling items from sample retention warehouse (backport [#55613](https://github.com/frappe/erpnext/issues/55613)) ([#55633](https://github.com/frappe/erpnext/issues/55633)) ([c15012c](c15012cd51))
* **process statement of accounts:** validate pdf_name and validate permission before triggering send_auto_email (backport [#55781](https://github.com/frappe/erpnext/issues/55781)) ([#55782](https://github.com/frappe/erpnext/issues/55782)) ([18ca96c](18ca96c36b))
* remove item name from update items dialog item code column (backport [#55718](https://github.com/frappe/erpnext/issues/55718)) ([#55722](https://github.com/frappe/erpnext/issues/55722)) ([09453f8](09453f883b))
* resolve conflict ([271ddb6](271ddb6add))
* restrict already invoiced qty in intercompany purchase invoice ([#55754](https://github.com/frappe/erpnext/issues/55754)) ([a5c23a3](a5c23a3d16))
* **selling:** consider delivered qty (backport [#55597](https://github.com/frappe/erpnext/issues/55597)) ([#55606](https://github.com/frappe/erpnext/issues/55606)) ([e8267e3](e8267e3237))
* simplify New Zealand sales accounts ([eebb37f](eebb37f9fd))
* sql injection ([a94e362](a94e362b8c))
* **stock:** add validation for work order seial nos and batch nos ([6d3f9d3](6d3f9d3c6f))
* update items respects workflow "Only Allow Edit For" role (backport [#55667](https://github.com/frappe/erpnext/issues/55667)) ([#55705](https://github.com/frappe/erpnext/issues/55705)) ([7852ea6](7852ea65af))
* use new_doc with field allowlist in CRM integration endpoints ([45b232d](45b232d369))
* validate fg and materials qty in the disassemble entry ([ba19a24](ba19a24526))
* work order status should be in process if material transfer is skipped (backport [#55641](https://github.com/frappe/erpnext/issues/55641) to version-15-hotfix) ([#55643](https://github.com/frappe/erpnext/issues/55643)) ([55b0715](55b0715310))

### Features

* add New Zealand chart of accounts ([f8a123e](f8a123e79d))

### Performance Improvements

* **transaction:** exit early before backend query (backport [#55556](https://github.com/frappe/erpnext/issues/55556)) ([#55557](https://github.com/frappe/erpnext/issues/55557)) ([ccbca57](ccbca57420))
2026-06-10 00:24:00 +00:00
Mihir Kandoi
316bb13853 Merge pull request #55763 from frappe/version-15-hotfix 2026-06-10 05:52:23 +05:30
mergify[bot]
18ca96c36b fix(process statement of accounts): validate pdf_name and validate permission before triggering send_auto_email (backport #55781) (#55782)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
fix(process statement of accounts): validate pdf_name and validate permission before triggering send_auto_email (#55781)
2026-06-09 19:10:24 +00:00
Mihir Kandoi
95f46dfc01 fix: handle multi-select stock ageing filters (#55776) 2026-06-09 13:45:52 +00:00
mergify[bot]
0e64acb0fa fix: prevent leakage of party-derived fields in cross doctype transactions (backport #55336) (#55578)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Lakshit Jain <ljain112@gmail.com>
fix: prevent leakage of party-derived fields in cross doctype transactions (#55336)
2026-06-09 13:23:49 +00:00
Pandiyan P
a5c23a3d16 fix: restrict already invoiced qty in intercompany purchase invoice (#55754) 2026-06-09 13:13:23 +05:30
mergify[bot]
2f5b93e308 fix(cheque_print_template): print format creation from cheque print template requires system manager (backport #55708) (#55711)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
fix(cheque_print_template): print format creation from cheque print template requires system manager (#55708)
2026-06-09 13:01:40 +05:30
rohitwaghchaure
aeed6a459f Merge pull request #55751 from frappe/mergify/bp/version-15-hotfix/pr-55748
fix: sql injection (backport #55748)
2026-06-09 09:49:16 +05:30
rohitwaghchaure
c0f56cd284 chore: fix conflicts
Removed conflicting code related to date_field assignment based on filters.
2026-06-09 09:31:58 +05:30
Rohit Waghchaure
a94e362b8c fix: sql injection
(cherry picked from commit bd0acf4413)

# Conflicts:
#	erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
2026-06-09 03:58:13 +00:00
rohitwaghchaure
a121048f9b Merge pull request #55741 from frappe/mergify/bp/version-15-hotfix/pr-55740
fix: validate fg and materials qty in the disassemble entry (backport #55740)
2026-06-08 22:45:45 +05:30
mergify[bot]
cedd0a1903 fix: bypass project permission check when updating consumed material … (backport #55645) (#55706)
* fix: bypass project permission check when updating consumed material cost

(cherry picked from commit 4b0b7adeee)

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

* chore: resolve conflicts

* chore: resolve conflicts

---------

Co-authored-by: pandiyan <pandiyanpalani37@gmail.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-06-08 14:22:54 +00:00
rohitwaghchaure
8bccc25b1a chore: fix conflicts
Removed unused test cases related to sample retention stock entry.
2026-06-08 19:45:39 +05:30
rohitwaghchaure
335173c5ba chore: fix conflicts
Reintroduce the _qty_tolerance function to handle float rounding.
2026-06-08 19:44:48 +05:30
Rohit Waghchaure
ba19a24526 fix: validate fg and materials qty in the disassemble entry
(cherry picked from commit 4453c1072a)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py
#	erpnext/stock/doctype/stock_entry/test_stock_entry.py
2026-06-08 14:01:31 +00:00
rohitwaghchaure
2c4b89d1df Merge pull request #55734 from frappe/mergify/bp/version-15-hotfix/pr-55716
fix: do not allow to make changes in SABB after submit (backport #55716)
2026-06-08 16:02:35 +05:30
ruthra kumar
14bafe9dfd Merge pull request #55728 from frappe/mergify/bp/version-15-hotfix/pr-55486
Validations in CRM-api endpoints (backport #55486)
2026-06-08 15:50:23 +05:30
ruthra kumar
77d9849d16 Merge pull request #55730 from frappe/mergify/bp/version-15-hotfix/pr-55487
fix: add validations in accounts whitelisted methods (backport #55487)
2026-06-08 15:49:34 +05:30
mergify[bot]
09453f883b fix: remove item name from update items dialog item code column (backport #55718) (#55722)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
Co-authored-by: Abdullah <frappe@LAPTOP-4E788RM4.localdomain>
fix: remove item name from update items dialog item code column (#55718)
2026-06-08 15:42:09 +05:30
rohitwaghchaure
dc1fb3f804 chore: fix conflicts
Removed unused methods related to stock ledger entries and negative stock handling.
2026-06-08 15:28:44 +05:30
Rohit Waghchaure
07b61113af fix: do not allow to make changes in SABB after submit
(cherry picked from commit e36426e235)

# Conflicts:
#	erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
2026-06-08 09:56:01 +00:00
Shllokkk
2d1c0dcb53 fix: add permission checks in accounts whitelisted methods
(cherry picked from commit 5dbf3fdde0)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.py
2026-06-08 15:17:26 +05:30
Shllokkk
45b232d369 fix: use new_doc with field allowlist in CRM integration endpoints
(cherry picked from commit e460e83516)
2026-06-08 09:41:05 +00:00
mergify[bot]
7852ea65af fix: update items respects workflow "Only Allow Edit For" role (backport #55667) (#55705)
* fix: update items respects workflow "Only Allow Edit For" role (#55667)

(cherry picked from commit 76b9b6a34e)

# Conflicts:
#	erpnext/controllers/accounts_controller.py

* chore: resolve conflicts

---------

Co-authored-by: kaulith <64089478+kaulith@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-06-08 06:59:05 +00:00
ruthra kumar
999cfef619 Merge pull request #55703 from frappe/mergify/bp/version-15-hotfix/pr-55665
fix: drop ignore_permissions handling from add_ac (backport #55665)
2026-06-08 12:01:48 +05:30
Shllokkk
2d0e3fd9af fix: drop ignore_permissions handling from add_ac
(cherry picked from commit 37d2adc74b)
2026-06-08 06:14:59 +00:00
mergify[bot]
4c9c2911a6 chore: remove unused whitelisted method from project (backport #55648) (#55672)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2026-06-07 23:15:42 +05:30
rohitwaghchaure
e0a0b3eafc Merge pull request #55663 from frappe/mergify/bp/version-15-hotfix/pr-55661
fix: naming series issue (backport #55661)
2026-06-05 21:04:00 +05:30
rohitwaghchaure
8c9168595f chore: fix conflicts
Removed unused import of DateTimeLikeObject.
2026-06-05 20:48:19 +05:30
Rohit Waghchaure
e7eaa87a77 fix: naming series issue
(cherry picked from commit 3a50056968)

# Conflicts:
#	erpnext/stock/doctype/batch/batch.py
2026-06-05 15:14:02 +00:00
Mihir Kandoi
ffb36cff78 [codex] Show in-transit status for add-to-transit Stock Entries (backport #55644 to version-15-hotfix) (#55659)
[codex] Show in-transit status for add-to-transit Stock Entries (#55644)

(cherry picked from commit df03524b19)
2026-06-05 11:32:33 +00:00
Khushi Rawat
4fadc4aab6 Merge pull request #55652 from khushi8112/address-contacts-report-field-order
fix: correct field order in Address and Contacts report
2026-06-05 16:13:24 +05:30
khushi8112
bde46cffd0 fix: correct field order in Address and Contacts report 2026-06-05 15:33:39 +05:30
khushi8112
08f3cf98f9 fix: correct field order in Address and Contacts report 2026-06-05 15:31:43 +05:30
Mihir Kandoi
55b0715310 fix: work order status should be in process if material transfer is skipped (backport #55641 to version-15-hotfix) (#55643)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 05:36:10 +00:00
mergify[bot]
c15012cd51 fix: prevent selling items from sample retention warehouse (backport #55613) (#55633)
Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: prevent selling items from sample retention warehouse (#55613)
2026-06-04 16:48:59 +00:00
Mihir Kandoi
e8e0514a30 fix: persist main item code for MR plan items (#55623) 2026-06-04 21:38:35 +05:30
mergify[bot]
ddaf75a60d refactor: convert rfq_transaction_list to query builder (backport #55497) (#55629)
* refactor: convert rfq_transaction_list to query builder (#55497)

(cherry picked from commit 9cecf2e6f9)

# Conflicts:
#	erpnext/controllers/website_list_for_contact.py

* chore: resolve conflicts

---------

Co-authored-by: Shllokkk <140623894+Shllokkk@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-06-04 14:26:49 +00:00
mergify[bot]
013bd1a566 fix: duplicating a Customer/Supplier shouldn't inherit the source's primary contact and address (backport #55421) (#55608)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Antoine Maas <antoine.maas@okte.io>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: duplicating a Customer/Supplier shouldn't inherit the source's primary contact and address (#55421)
2026-06-03 16:04:25 +00:00
mergify[bot]
e8267e3237 fix(selling): consider delivered qty (backport #55597) (#55606)
Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
fix(selling): consider delivered qty (#55597)
2026-06-03 15:37:00 +00:00
rohitwaghchaure
d5477b096d Merge pull request #55604 from aerele/backport-54785
fix(stock): add validation for work order seial nos and batch nos
2026-06-03 19:12:49 +05:30
pandiyan
6d3f9d3c6f fix(stock): add validation for work order seial nos and batch nos 2026-06-03 18:39:22 +05:30
Khushi Rawat
f65b56d73f Merge pull request #55587 from frappe/mergify/bp/version-15-hotfix/pr-55150
fix(accounts): include asset items in purchase receipt validation (backport #55150)
2026-06-03 15:35:52 +05:30
Khushi Rawat
271ddb6add fix: resolve conflict 2026-06-03 14:22:59 +05:30
mergify[bot]
9095c5a3c2 regional(setup): add 0% and 6% VAT rates for Belgium (backport #54719) (#55582)
Co-authored-by: Antoine Maas <antoine.maas@okte.io>
2026-06-03 14:11:51 +05:30
mergify[bot]
0a7c3581da Avoid status updation for purchase invoice from paid to unpaid by issuing a paid debit note against it (backport #54382) (#55575)
Co-authored-by: Shllokkk <140623894+Shllokkk@users.noreply.github.com>
2026-06-03 14:11:12 +05:30
Pandiyan P
611849f953 fix(accounts): include asset items in purchase receipt validation (#55150)
(cherry picked from commit d0d9411700)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
2026-06-03 07:43:14 +00:00
ruthra kumar
5a80de43fa Merge pull request #55478 from IMS94/feat-new-zealand-chart-of-accounts-v15
feat: add New Zealand chart of accounts
2026-06-03 11:17:01 +05:30
mergify[bot]
4772799db2 fix(item): format integer numeric variant attributes without decimals (backport #55561) (#55563)
* fix(item): format integer numeric variant attributes without decimals (#55561)

(cherry picked from commit 016b64df6d)

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

* chore: resolve conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2026-06-02 22:59:43 +02:00
mergify[bot]
ccbca57420 perf(transaction): exit early before backend query (backport #55556) (#55557)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2026-06-02 21:41:47 +02:00
Frappe PR Bot
d6b2fb2f96 chore(release): Bumped to Version 15.110.0
# [15.110.0](https://github.com/frappe/erpnext/compare/v15.109.3...v15.110.0) (2026-06-02)

### Bug Fixes

* billing address does not belongs to the company error ([5c392d6](5c392d6123))
* **book_appointment:** when scheduling is disabled, block API endpoints (backport [#55455](https://github.com/frappe/erpnext/issues/55455)) ([#55456](https://github.com/frappe/erpnext/issues/55456)) ([2a12ae1](2a12ae1afe))
* check perm for account (backport [#55479](https://github.com/frappe/erpnext/issues/55479)) ([#55482](https://github.com/frappe/erpnext/issues/55482)) ([1238aeb](1238aeb30a))
* **issue:** check permission before issue status modification (backport [#55458](https://github.com/frappe/erpnext/issues/55458)) ([#55459](https://github.com/frappe/erpnext/issues/55459)) ([338feb3](338feb31e1))
* **je:** preserve account on duplicate row when party row exists (backport [#55180](https://github.com/frappe/erpnext/issues/55180)) ([#55513](https://github.com/frappe/erpnext/issues/55513)) ([741216d](741216d3eb))
* **manufacturing:** allow to edit batch size while creating a work order ([#55332](https://github.com/frappe/erpnext/issues/55332)) ([41bf2f3](41bf2f32fd))
* material transfer in transit issue (backport [#55320](https://github.com/frappe/erpnext/issues/55320)) ([#55324](https://github.com/frappe/erpnext/issues/55324)) ([067c23f](067c23f20e))
* new bom version should not recalculate operations through routing (backport [#55370](https://github.com/frappe/erpnext/issues/55370)) ([#55371](https://github.com/frappe/erpnext/issues/55371)) ([4669ff2](4669ff295f))
* pick correct name when creating user from RFQ (backport [#55468](https://github.com/frappe/erpnext/issues/55468)) ([#55471](https://github.com/frappe/erpnext/issues/55471)) ([e429e60](e429e608c2))
* **pos:** escape html output in pos page templates (backport [#55527](https://github.com/frappe/erpnext/issues/55527)) ([#55528](https://github.com/frappe/erpnext/issues/55528)) ([689a3f5](689a3f50ae))
* **pos:** escape item data on pos item selector (backport [#55503](https://github.com/frappe/erpnext/issues/55503)) ([#55523](https://github.com/frappe/erpnext/issues/55523)) ([96bd97d](96bd97dd6d))
* **pos:** preserve contacts and enforce permissions in set_customer_info (backport [#55463](https://github.com/frappe/erpnext/issues/55463)) ([#55465](https://github.com/frappe/erpnext/issues/55465)) ([0353262](03532624b8))
* **ppr:** make default_advance_account optional ([aa94c3f](aa94c3ff22))
* **quotation:** made customer contact column visible (backport [#55433](https://github.com/frappe/erpnext/issues/55433)) ([#55434](https://github.com/frappe/erpnext/issues/55434)) ([a2d924c](a2d924c48f))
* **regional:** Japanese CT Rate (backport [#54998](https://github.com/frappe/erpnext/issues/54998)) ([#55437](https://github.com/frappe/erpnext/issues/55437)) ([2a52ea6](2a52ea6850))
* replace get_query with get_list for permission-aware queries in v15 ([ad511b8](ad511b80c0))
* stock reco for legacy serial nos ([93dcba4](93dcba40ec))
* **stock:** add warning message to notify the user to configure the inspection ([42e2fd5](42e2fd5fc9))
* **stock:** allow to create quality inspection after purchase/delivery ([10664b7](10664b7b95))
* **stock:** change qb to qb get_query to fix filter issues (backport [#55443](https://github.com/frappe/erpnext/issues/55443)) ([#55444](https://github.com/frappe/erpnext/issues/55444)) ([75d00ef](75d00ef173))
* **stock:** change valuation rate column label in stock ledger entry/report (backport [#55323](https://github.com/frappe/erpnext/issues/55323)) ([#55393](https://github.com/frappe/erpnext/issues/55393)) ([94fd15e](94fd15e550))
* **stock:** get_actual_qty during cancellations (backport [#55388](https://github.com/frappe/erpnext/issues/55388)) ([#55391](https://github.com/frappe/erpnext/issues/55391)) ([ad6e3a4](ad6e3a45d2))
* update default_advance_account type ([7200c22](7200c22890))
* use get_query instead of get_all for data fetching ([264433b](264433b23d))

### Features

* **payment-entry:** warn user before cancelling reconciled payment entry ([87c6ad4](87c6ad4f85))
2026-06-02 16:55:43 +00:00
Mihir Kandoi
a6b7142c18 Merge pull request #55546 from frappe/version-15-hotfix 2026-06-02 22:24:03 +05:30
Khushi Rawat
6796617921 Merge pull request #55542 from frappe/mergify/bp/version-15-hotfix/pr-55539
feat(payment-entry): warn user before cancelling reconciled payment entry (backport #55539)
2026-06-02 17:09:59 +05:30
ruthra kumar
c65d768020 Merge pull request #55544 from frappe/mergify/bp/version-15-hotfix/pr-54979
fix(ppr): make default_advance_account optional (backport #54979)
2026-06-02 16:48:42 +05:30
Dany Robert
7200c22890 fix: update default_advance_account type
(cherry picked from commit 30b9e11303)
2026-06-02 10:06:40 +00:00
Dany Robert
aa94c3ff22 fix(ppr): make default_advance_account optional
(cherry picked from commit 4b1d369ac6)
2026-06-02 10:06:39 +00:00
khushi8112
87c6ad4f85 feat(payment-entry): warn user before cancelling reconciled payment entry
(cherry picked from commit f0ba54d957)
2026-06-02 10:00:11 +00:00
Khushi Rawat
35b4ada3e2 Merge pull request #55358 from frappe/mergify/bp/version-15-hotfix/pr-55137
fix: use get_query instead of get_all for data fetching (backport #55137)
2026-06-02 13:19:11 +05:30
khushi8112
ad511b80c0 fix: replace get_query with get_list for permission-aware queries in v15 2026-06-02 12:17:37 +05:30
rohitwaghchaure
ad55c7c372 Merge pull request #55518 from frappe/mergify/bp/version-15-hotfix/pr-55415
fix(stock): allow to create quality inspection after purchase/delivery (backport #55415)
2026-06-02 11:52:05 +05:30
Diptanil Saha
689a3f50ae fix(pos): escape html output in pos page templates (backport #55527) (#55528)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 00:42:59 +05:30
Diptanil Saha
96bd97dd6d fix(pos): escape item data on pos item selector (backport #55503) (#55523) 2026-06-01 22:17:44 +05:30
rohitwaghchaure
897722c35f chore: fixed conflicts 2026-06-01 22:14:46 +05:30
rohitwaghchaure
54cbc91166 chore: fixed conflicts 2026-06-01 22:11:58 +05:30
mergify[bot]
ecf9aa146c chore(serial_and_batch_bundle): remove update_serial_or_batch method (backport #55481) (#55515)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-06-01 16:40:41 +00:00
Sudharsanan11
42e2fd5fc9 fix(stock): add warning message to notify the user to configure the inspection
(cherry picked from commit e003fe4de0)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
2026-06-01 16:13:51 +00:00
Sudharsanan11
10664b7b95 fix(stock): allow to create quality inspection after purchase/delivery
(cherry picked from commit c6a88ab1d2)

# Conflicts:
#	erpnext/controllers/stock_controller.py
#	erpnext/public/js/controllers/transaction.js
2026-06-01 16:13:50 +00:00
ruthra kumar
1980307048 Merge pull request #55514 from ruthra-kumar/remove_flaky_process_pcv_test
refactor(test): remove flaky test in process pcv
2026-06-01 19:35:07 +05:30
Frappe PR Bot
13eeddd1f6 chore(release): Bumped to Version 15.109.3
## [15.109.3](https://github.com/frappe/erpnext/compare/v15.109.2...v15.109.3) (2026-06-01)

### Bug Fixes

* only consider non-opening balance for Balance sheet accounts ([4a6af25](4a6af25d11))
2026-06-01 14:01:43 +00:00
ruthra kumar
dc08b615f1 Merge pull request #55501 from frappe/mergify/bp/version-15/pr-55495
fix: opening bal double counting in Process Period Closing Voucher (backport #55495)
2026-06-01 19:30:03 +05:30
ruthra kumar
ce94f4fd11 refactor(test): remove flaky test in process pcv 2026-06-01 19:15:50 +05:30
ruthra kumar
e314d0cfc5 refactor: color coded status in list view
(cherry picked from commit cfeffbb354)
2026-06-01 19:13:36 +05:30
mergify[bot]
741216d3eb fix(je): preserve account on duplicate row when party row exists (backport #55180) (#55513)
fix(je): preserve account on duplicate row when party row exists (#55180)

(cherry picked from commit 57dbac712f)

Co-authored-by: Gajendra Nishad <75714258+gajjug004@users.noreply.github.com>
2026-06-01 18:46:21 +05:30
ruthra kumar
94e15ae9ef refactor: tabbed view for process period closing voucher
(cherry picked from commit 1960c81619)
2026-06-01 18:09:46 +05:30
ruthra kumar
4a6af25d11 fix: only consider non-opening balance for Balance sheet accounts
(cherry picked from commit a2b8334046)
2026-06-01 18:09:41 +05:30
ruthra kumar
c7fbc133e6 Merge pull request #55498 from frappe/mergify/bp/version-15-hotfix/pr-55495
fix: opening bal double counting in Process Period Closing Voucher (backport #55495)
2026-06-01 17:50:32 +05:30
ruthra kumar
e5aa45cf0d test: prevent double counting of opening balances
(cherry picked from commit 7f2af123ee)
2026-06-01 17:50:10 +05:30
ruthra kumar
3e3689d938 refactor: color coded status in list view
(cherry picked from commit cfeffbb354)
2026-06-01 15:10:00 +05:30
ruthra kumar
d0fc3f029f refactor: tabbed view for process period closing voucher
(cherry picked from commit 1960c81619)
2026-06-01 15:09:57 +05:30
ruthra kumar
a9cfa22199 refactor: only consider non-opening balance for Balance sheet accounts
(cherry picked from commit a2b8334046)
2026-06-01 09:32:45 +00:00
mergify[bot]
1238aeb30a fix: check perm for account (backport #55479) (#55482)
fix: check perm for account (#55479)

(cherry picked from commit dd1d2925d5)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2026-06-01 14:29:35 +05:30
Imesha Sudasingha
eebb37f9fd fix: simplify New Zealand sales accounts 2026-06-01 13:22:06 +05:30
Imesha Sudasingha
f8a123e79d feat: add New Zealand chart of accounts 2026-06-01 12:03:59 +05:30
Frappe PR Bot
1c5220b86f chore(release): Bumped to Version 15.109.2
## [15.109.2](https://github.com/frappe/erpnext/compare/v15.109.1...v15.109.2) (2026-06-01)

### Bug Fixes

* billing address does not belongs to the company error ([c2063c4](c2063c4707))
2026-06-01 06:08:30 +00:00
rohitwaghchaure
779f1b6104 Merge pull request #55474 from frappe/mergify/bp/version-15/pr-55424
fix: billing address does not belongs to the company error (backport #55417) (backport #55424)
2026-06-01 11:36:58 +05:30
Rohit Waghchaure
c2063c4707 fix: billing address does not belongs to the company error
(cherry picked from commit 9df07b367a)
(cherry picked from commit 5c392d6123)
2026-06-01 06:01:45 +00:00
mergify[bot]
e429e608c2 fix: pick correct name when creating user from RFQ (backport #55468) (#55471)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: pick correct name when creating user from RFQ (#55468)
2026-06-01 05:52:40 +00:00
mergify[bot]
75d00ef173 fix(stock): change qb to qb get_query to fix filter issues (backport #55443) (#55444)
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
fix(stock): change qb to qb get_query to fix filter issues (#55443)
2026-06-01 05:33:25 +00:00
mergify[bot]
94fd15e550 fix(stock): change valuation rate column label in stock ledger entry/report (backport #55323) (#55393)
Co-authored-by: Sudharsanan11 <sudharsananashok1975@gmail.com>
2026-06-01 05:10:27 +00:00
mergify[bot]
03532624b8 fix(pos): preserve contacts and enforce permissions in set_customer_info (backport #55463) (#55465)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
fix(pos): preserve contacts and enforce permissions in set_customer_info (#55463)
2026-06-01 05:18:34 +05:30
mergify[bot]
338feb31e1 fix(issue): check permission before issue status modification (backport #55458) (#55459)
* fix(issue): check permission before issue status modification (#55458)

(cherry picked from commit 876f403500)

# Conflicts:
#	erpnext/support/doctype/issue/issue.py

* chore: resolve conflicts

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2026-05-31 19:06:01 +00:00
mergify[bot]
2a805e090c refactor: task_info portal pages (backport #55448) (#55453)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2026-05-31 22:07:01 +05:30
mergify[bot]
2a12ae1afe fix(book_appointment): when scheduling is disabled, block API endpoints (backport #55455) (#55456)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
fix(book_appointment): when scheduling is disabled, block API endpoints (#55455)
2026-05-31 16:06:25 +00:00
mergify[bot]
715ca39abc refactor(pos_profile): migrating raw sql to qb in set_defaults (backport #55447) (#55449)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2026-05-31 09:40:26 +00:00
Raffael Meyer
cad14ac3e6 chore: mark as out of beta (backport #55439) (#55440) 2026-05-30 19:09:45 +00:00
mergify[bot]
2a52ea6850 fix(regional): Japanese CT Rate (backport #54998) (#55437)
Co-authored-by: mh35 <mh35jp@gmail.com>
fix(regional): Japanese CT Rate (#54998)
2026-05-30 22:00:19 +05:30
mergify[bot]
a2d924c48f fix(quotation): made customer contact column visible (backport #55433) (#55434)
* fix(quotation): made customer contact column visible (#55433)

(cherry picked from commit 9758eb868d)

# Conflicts:
#	erpnext/selling/doctype/quotation/quotation.json

* chore: resolved conflicts

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2026-05-30 14:20:40 +00:00
rohitwaghchaure
3ad39c987b Merge pull request #55424 from frappe/mergify/bp/version-15-hotfix/pr-55417
fix: billing address does not belongs to the company error (backport #55417)
2026-05-30 12:43:31 +05:30
Rohit Waghchaure
5c392d6123 fix: billing address does not belongs to the company error
(cherry picked from commit 9df07b367a)
2026-05-29 17:23:55 +00:00
Frappe PR Bot
9e7b03173d chore(release): Bumped to Version 15.109.1
## [15.109.1](https://github.com/frappe/erpnext/compare/v15.109.0...v15.109.1) (2026-05-29)

### Bug Fixes

* material transfer in transit issue (backport [#55320](https://github.com/frappe/erpnext/issues/55320)) (backport [#55324](https://github.com/frappe/erpnext/issues/55324)) ([#55404](https://github.com/frappe/erpnext/issues/55404)) ([bfdf1e4](bfdf1e43f9))
2026-05-29 12:07:01 +00:00
mergify[bot]
bfdf1e43f9 fix: material transfer in transit issue (backport #55320) (backport #55324) (#55404)
Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
fix: material transfer in transit issue (backport #55320) (#55324)
2026-05-29 17:35:20 +05:30
mergify[bot]
ad6e3a45d2 fix(stock): get_actual_qty during cancellations (backport #55388) (#55391)
Co-authored-by: archielister <archie.lister@lush.co.uk>
fix(stock): get_actual_qty during cancellations (#55388)
2026-05-28 22:40:11 +05:30
mergify[bot]
4669ff295f fix: new bom version should not recalculate operations through routing (backport #55370) (#55371)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: new bom version should not recalculate operations through routing (#55370)
2026-05-28 16:01:36 +05:30
khushi8112
264433b23d fix: use get_query instead of get_all for data fetching
(cherry picked from commit 1fd99337b3)
2026-05-27 19:17:02 +00:00
Pandiyan P
41bf2f32fd fix(manufacturing): allow to edit batch size while creating a work order (#55332) 2026-05-27 18:39:59 +05:30
rohitwaghchaure
a6d4bc5c86 Merge pull request #55298 from frappe/mergify/bp/version-15-hotfix/pr-55242
fix: stock reco for legacy serial nos (backport #55242)
2026-05-27 12:15:19 +05:30
Rohit Waghchaure
93dcba40ec fix: stock reco for legacy serial nos
(cherry picked from commit 9d5fd11bcd)
2026-05-27 11:50:30 +05:30
mergify[bot]
067c23f20e fix: material transfer in transit issue (backport #55320) (#55324)
Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2026-05-27 11:45:23 +05:30
Frappe PR Bot
16fbf8299f chore(release): Bumped to Version 15.109.0
# [15.109.0](https://github.com/frappe/erpnext/compare/v15.108.3...v15.109.0) (2026-05-27)

### Bug Fixes

* consider batchwise valuation in stock ageing report (backport [#54919](https://github.com/frappe/erpnext/issues/54919)) ([#55229](https://github.com/frappe/erpnext/issues/55229)) ([418a7fb](418a7fb301))
* consumed operation cost calculation (backport [#54858](https://github.com/frappe/erpnext/issues/54858)) ([#55132](https://github.com/frappe/erpnext/issues/55132)) ([46d5395](46d5395148))
* default use_for_shopping_cart to 0 in set_taxes ([960be3e](960be3e081))
* edit stock uom qty for purchase documents (backport [#55135](https://github.com/frappe/erpnext/issues/55135)) ([#55178](https://github.com/frappe/erpnext/issues/55178)) ([425e6c5](425e6c52f4))
* ERPNextTestSuite to change_settings ([76078a7](76078a7fb9))
* faster range calculation on process period closing voucher ([bf27f2d](bf27f2d869))
* fg valuation rate in repack entry when multiple FGs ([238f168](238f1685f1))
* **general-ledger:** show raw GL entries when categorize_by is empty (backport [#54816](https://github.com/frappe/erpnext/issues/54816)) ([#54829](https://github.com/frappe/erpnext/issues/54829)) ([b972b7c](b972b7c307))
* import change_settings ([9d21199](9d211990c3))
* inclusive tax amount not considered while setting LCV from purchase invoice ([cba4c9f](cba4c9f0ee))
* incoming rate for legacy serial no ([6e6ef83](6e6ef83d60))
* incorrect error message string in sales order (backport [#55090](https://github.com/frappe/erpnext/issues/55090)) ([#55094](https://github.com/frappe/erpnext/issues/55094)) ([04e28f9](04e28f9556))
* invalid filter on item_group (backport [#55186](https://github.com/frappe/erpnext/issues/55186)) ([#55187](https://github.com/frappe/erpnext/issues/55187)) ([25739ae](25739ae217))
* merge conflicts ([59e9f51](59e9f5192c))
* **payment_entry:** sync paid/received amounts for cross-currency entries (backport [#55270](https://github.com/frappe/erpnext/issues/55270)) ([#55271](https://github.com/frappe/erpnext/issues/55271)) ([d31a051](d31a051c74))
* prevent AttributeError in batch query filters (backport [#55257](https://github.com/frappe/erpnext/issues/55257)) ([#55278](https://github.com/frappe/erpnext/issues/55278)) ([4f89f3a](4f89f3a856))
* **project:** update customer and sales order as no copy ([9145760](914576040e))
* removed redundant code ([259f499](259f499e25))
* set bin details when adding item using update items (backport [#55096](https://github.com/frappe/erpnext/issues/55096)) ([#55097](https://github.com/frappe/erpnext/issues/55097)) ([aa79247](aa79247c39))
* single variant creation error (backport [#55286](https://github.com/frappe/erpnext/issues/55286)) ([#55288](https://github.com/frappe/erpnext/issues/55288)) ([937eb87](937eb87932))
* **stock:** apply posting datetime filters while fetching available batches (backport [#54976](https://github.com/frappe/erpnext/issues/54976)) ([#55184](https://github.com/frappe/erpnext/issues/55184)) ([ff442cd](ff442cd8e7))
* **stock:** remove precision for valuation rate while creating sle (backport [#55249](https://github.com/frappe/erpnext/issues/55249)) ([#55259](https://github.com/frappe/erpnext/issues/55259)) ([8b241b4](8b241b45e2))
* **stock:** remove recalculate current qty function ([#55121](https://github.com/frappe/erpnext/issues/55121)) ([1c90c3b](1c90c3bbc2))
* update import ([31c251d](31c251d956))
* use passed posting date in make_reverse_gl_entries ([4436585](4436585aa0))

### Features

* add get_parent_supplier_groups using query builder ([6517ed7](6517ed72b4))

### Performance Improvements

* skip delink_original_entry during cancellation when Immutable Ledger is enabled ([#55130](https://github.com/frappe/erpnext/issues/55130)) ([034e159](034e159ee4))
2026-05-27 00:23:04 +00:00
Diptanil Saha
7ce7e3d5e5 Merge pull request #55316 from frappe/version-15-hotfix
chore: release v15
2026-05-27 05:51:33 +05:30
Diptanil Saha
60fdc6bc1a Merge branch 'version-15' into version-15-hotfix 2026-05-27 05:31:52 +05:30
mergify[bot]
b972b7c307 fix(general-ledger): show raw GL entries when categorize_by is empty (backport #54816) (#54829)
fix(general-ledger): show raw GL entries when categorize_by is empty (#54816)

(cherry picked from commit dfbe847307)

# Conflicts:
#	erpnext/accounts/report/general_ledger/general_ledger.py

Co-authored-by: Jatin3128 <140256508+Jatin3128@users.noreply.github.com>
2026-05-26 23:34:47 +00:00
Nihantra C. Patel
4927d346c8 Merge pull request #55294 from frappe/mergify/bp/version-15-hotfix/pr-55268
fix: use passed posting date for period closing validation in reverse GL entries (backport #55268)
2026-05-26 23:13:14 +05:30
Nihantra Patel
8f164cff1d test: immutable ledger reverse entry 2026-05-26 22:53:14 +05:30
Nihantra C. Patel
31c251d956 fix: update import 2026-05-26 22:17:23 +05:30
rohitwaghchaure
bc81992a40 Merge pull request #55296 from frappe/mergify/bp/version-15-hotfix/pr-55290
fix: inclusive tax amount not considered while setting LCV from purchase invoice (backport #55290)
2026-05-26 16:23:13 +05:30
rohitwaghchaure
66267cf99a chore: fix conflicts 2026-05-26 15:57:24 +05:30
Nihantra C. Patel
9d211990c3 fix: import change_settings 2026-05-26 15:56:38 +05:30
Nihantra C. Patel
76078a7fb9 fix: ERPNextTestSuite to change_settings 2026-05-26 15:48:41 +05:30
Rohit Waghchaure
cba4c9f0ee fix: inclusive tax amount not considered while setting LCV from purchase invoice
(cherry picked from commit 048ddfc265)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
2026-05-26 10:15:01 +00:00
mergify[bot]
46d5395148 fix: consumed operation cost calculation (backport #54858) (#55132)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: consumed operation cost calculation (#54858)
2026-05-26 10:14:19 +00:00
Nihantra Patel
b8b2141e20 test: update testcase
(cherry picked from commit 9c39b01f1c)
2026-05-26 10:13:33 +00:00
Nihantra Patel
4436585aa0 fix: use passed posting date in make_reverse_gl_entries
(cherry picked from commit f040bdf165)
2026-05-26 10:13:33 +00:00
mergify[bot]
937eb87932 fix: single variant creation error (backport #55286) (#55288)
* fix: single variant creation error

(cherry picked from commit bda75135c3)

* feat: allow creation of any number of variants in multiple item variant creation dialog

(cherry picked from commit 090c25d848)

# Conflicts:
#	erpnext/controllers/item_variant.py

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-26 08:25:21 +00:00
ruthra kumar
6a21d28030 Merge pull request #55280 from frappe/mergify/bp/version-15-hotfix/pr-55256
refactor: handle processes stuck in running state in process pcv (backport #55256)
2026-05-26 10:52:25 +05:30
mergify[bot]
4f89f3a856 fix: prevent AttributeError in batch query filters (backport #55257) (#55278)
Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
fix: prevent AttributeError in batch query filters (#55257)
2026-05-26 05:11:11 +00:00
mergify[bot]
8b241b45e2 fix(stock): remove precision for valuation rate while creating sle (backport #55249) (#55259)
Co-authored-by: Sudharsanan11 <sudharsananashok1975@gmail.com>
2026-05-26 10:29:27 +05:30
ruthra kumar
b517f26085 refactor: atomic summarization step for process pcv
(cherry picked from commit 6cb7971342)
2026-05-26 04:57:58 +00:00
ruthra kumar
f28b948e1b refactor: handle processes stuck in running state in process pcv
(cherry picked from commit f414778486)
2026-05-26 04:57:57 +00:00
ruthra kumar
a797ab3482 refactor: summarize in background
(cherry picked from commit 1c3a9f7dd9)
2026-05-26 04:57:57 +00:00
mergify[bot]
d31a051c74 fix(payment_entry): sync paid/received amounts for cross-currency entries (backport #55270) (#55271)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(payment_entry): sync paid/received amounts for cross-currency entries (#55270)
2026-05-25 23:21:43 +05:30
rohitwaghchaure
aad270914a Merge pull request #55243 from frappe/mergify/bp/version-15-hotfix/pr-55216
fix: fg valuation rate in repack entry when multiple FGs (backport #55216)
2026-05-25 15:34:30 +05:30
mergify[bot]
af3e7f53ac refactor: stock ageing report (backport #55231) (#55236)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-25 12:44:55 +05:30
Rohit Waghchaure
238f1685f1 fix: fg valuation rate in repack entry when multiple FGs
(cherry picked from commit a47e4c04f7)
2026-05-25 06:15:07 +00:00
mergify[bot]
418a7fb301 fix: consider batchwise valuation in stock ageing report (backport #54919) (#55229)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-24 09:48:12 +00:00
Nishka Gosalia
304474d2f7 Merge pull request #55195 from frappe/mergify/bp/version-15-hotfix/pr-55189
fix(project): update customer and sales order as no copy (backport #55189)
2026-05-23 16:00:12 +05:30
Nishka Gosalia
59e9f5192c fix: merge conflicts 2026-05-23 15:35:19 +05:30
nareshkannasln
914576040e fix(project): update customer and sales order as no copy
(cherry picked from commit 9d8f3863f2)

# Conflicts:
#	erpnext/projects/doctype/project/project.json
2026-05-22 12:21:04 +00:00
mergify[bot]
ff442cd8e7 fix(stock): apply posting datetime filters while fetching available batches (backport #54976) (#55184)
fix(stock): apply posting datetime filters while fetching available batches

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-22 11:34:43 +00:00
mergify[bot]
25739ae217 fix: invalid filter on item_group (backport #55186) (#55187)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: invalid filter on item_group (#55186)
2026-05-22 16:49:10 +05:30
mergify[bot]
425e6c52f4 fix: edit stock uom qty for purchase documents (backport #55135) (#55178)
Co-authored-by: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com>
fix: edit stock uom qty for purchase documents (#55135)
2026-05-22 09:12:19 +00:00
ruthra kumar
97d2152a36 Merge pull request #55165 from frappe/mergify/bp/version-15-hotfix/pr-55130
perf: skip delink_original_entry during cancellation when Immutable Ledger is enabled (backport #55130)
2026-05-22 14:26:00 +05:30
Nihantra C. Patel
034e159ee4 perf: skip delink_original_entry during cancellation when Immutable Ledger is enabled (#55130)
* perf: get payment ledger and remove update from delink when immutable ledger is enabled

* revert: changes of get_payment_ledger_entries

* perf: skip delink_original_entry during cancellation when Immutable Ledger is enabled

* test: for immutable ledger

* test: add posting_date in create_sales_invoice

* fix: link validation err with immutable ledger on

* test: update testcase of the immutable ledger

* refactor(test): simpler test for immutable invariants

---------

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
(cherry picked from commit 9eeccecd30)

# Conflicts:
#	erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py
#	erpnext/accounts/general_ledger.py
2026-05-22 12:52:49 +05:30
diptanilsaha
fff023bf7b Merge pull request #55143 from frappe/mergify/bp/version-15-hotfix/pr-55127
refactor: migrate get_tax_template to query builder with hierarchical group matching (backport #55127)
2026-05-22 02:32:22 +05:30
mergify[bot]
429e02e6f9 chore: migrate Address/Contact custom fields from JSON fixtures to install (backport #55084) (#55087)
fixtures to install (backport #55084)
2026-05-21 20:41:36 +00:00
diptanilsaha
eb96f0429f test: add tests for supplier group hierarchy and use_for_shopping_cart filter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 8c43118725)
2026-05-22 02:08:38 +05:30
diptanilsaha
960be3e081 fix: default use_for_shopping_cart to 0 in set_taxes
Ensures regular transactions only match tax rules where
use_for_shopping_cart = 0, preventing webshop-specific rules
from applying to standard documents.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 4d43c74f5f)
2026-05-22 02:08:38 +05:30
diptanilsaha
2a91c7229a refactor: rewrite get_tax_template using query builder
Migrates from raw frappe.db.sql with string interpolation to frappe.qb.
Adds hierarchical supplier_group matching (mirrors customer_group behaviour).
Removes unused get_customer_group_condition helper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit f98975f51a)
2026-05-22 02:08:26 +05:30
diptanilsaha
6517ed72b4 feat: add get_parent_supplier_groups using query builder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit cb610b79d2)
2026-05-21 11:35:54 +00:00
diptanilsaha
c125d1489c refactor: migrate get_parent_customer_groups to query builder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 91a2a7b0a0)
2026-05-21 11:35:54 +00:00
rohitwaghchaure
be1f1e8781 Merge pull request #55138 from frappe/mergify/bp/version-15-hotfix/pr-55134
fix: removed redundant code (backport #55134)
2026-05-21 16:17:03 +05:30
Rohit Waghchaure
259f499e25 fix: removed redundant code
(cherry picked from commit 14b17cd8a6)
2026-05-21 09:55:19 +00:00
rohitwaghchaure
fc05c38b9b Merge pull request #54977 from frappe/mergify/bp/version-15-hotfix/pr-54962
fix: incoming rate for legacy serial no (backport #54962)
2026-05-21 15:00:09 +05:30
rohitwaghchaure
da8d25d80a chore: fix linters issue
Added a setting to control fetching incoming rates for serial numbers.
2026-05-21 14:38:05 +05:30
rohitwaghchaure
6981599103 chore: fix conflicts
Removed fields related to parallel reposting and column breaks, and updated the modified date.
2026-05-21 14:31:22 +05:30
rohitwaghchaure
5557e982bf chore: fix conflicts
Removed legacy fields related to incoming rate and parallel reposting.
2026-05-21 14:30:26 +05:30
rohitwaghchaure
08466218d8 chore: fix conflicts
Removed legacy code for fetching incoming rates from serial numbers.
2026-05-21 14:28:29 +05:30
Pandiyan P
1c90c3bbc2 fix(stock): remove recalculate current qty function (#55121) 2026-05-21 06:11:41 +00:00
mergify[bot]
aa79247c39 fix: set bin details when adding item using update items (backport #55096) (#55097)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: set bin details when adding item using update items (#55096)
2026-05-20 16:21:25 +05:30
mergify[bot]
04e28f9556 fix: incorrect error message string in sales order (backport #55090) (#55094)
Co-authored-by: Shllokkk <140623894+Shllokkk@users.noreply.github.com>
fix: incorrect error message string in sales order (#55090)
2026-05-20 09:31:21 +00:00
Frappe PR Bot
519e409c1d chore(release): Bumped to Version 15.108.3
## [15.108.3](https://github.com/frappe/erpnext/compare/v15.108.2...v15.108.3) (2026-05-20)

### Bug Fixes

* faster range calculation on process period closing voucher ([5bd8132](5bd8132630))
2026-05-20 07:21:47 +00:00
ruthra kumar
3b748abfe5 Merge pull request #55081 from frappe/mergify/bp/version-15/pr-55072
perf: faster opening balance range calculation in process period closing voucher (backport #55072)
2026-05-20 12:50:17 +05:30
ruthra kumar
57d1f27e84 refactor: ppcv select with for update and skip locked
(cherry picked from commit eba58b2837)
2026-05-20 06:50:32 +00:00
ruthra kumar
5bd8132630 fix: faster range calculation on process period closing voucher
(cherry picked from commit ee33574a6d)
2026-05-20 06:50:31 +00:00
ruthra kumar
d666871d86 Merge pull request #55077 from frappe/mergify/bp/version-15-hotfix/pr-55072
perf: faster opening balance range calculation in process period closing voucher (backport #55072)
2026-05-20 12:08:30 +05:30
ruthra kumar
d81b6ab5dc refactor: ppcv select with for update and skip locked
(cherry picked from commit eba58b2837)
2026-05-20 06:18:41 +00:00
ruthra kumar
bf27f2d869 fix: faster range calculation on process period closing voucher
(cherry picked from commit ee33574a6d)
2026-05-20 06:18:41 +00:00
Frappe PR Bot
cf337824e7 chore(release): Bumped to Version 15.108.2
## [15.108.2](https://github.com/frappe/erpnext/compare/v15.108.1...v15.108.2) (2026-05-20)

### Bug Fixes

* add warehouse vaildation for repack entry (backport [#54866](https://github.com/frappe/erpnext/issues/54866)) ([#54900](https://github.com/frappe/erpnext/issues/54900)) ([b969662](b969662b6c))
* item leaderboard uses Sales/Purchase Invoice instead of Orders ([#55038](https://github.com/frappe/erpnext/issues/55038)) ([0b41df5](0b41df5ac8))
* merge conflicts ([8512eb4](8512eb4493))
* normalize date comparison to avoid datatype mismatch ([49b4830](49b4830785))
* **patch:** drop dead procedures first before other changes ([67d6761](67d67616ca))
* **payment_entry:** fix paid/received amount calculation for multi-currency accounts (backport [#54963](https://github.com/frappe/erpnext/issues/54963)) ([#54969](https://github.com/frappe/erpnext/issues/54969)) ([651af67](651af67b26))
* remove sql procedure method from AR report ([c705a93](c705a93776))
* stock balance showing incorrect value because of incorrect SLE ([dbacfd1](dbacfd13b8))
* **stock:** add whole number quantity validation in Stock Reconciliation (backport [#54922](https://github.com/frappe/erpnext/issues/54922)) ([#54924](https://github.com/frappe/erpnext/issues/54924)) ([48ed078](48ed07816d))
* **stock:** update buying amount calculation in gross profit report (backport [#55020](https://github.com/frappe/erpnext/issues/55020)) ([#55023](https://github.com/frappe/erpnext/issues/55023)) ([5e1880f](5e1880f09e))
* toast message for item price insert ([#55009](https://github.com/frappe/erpnext/issues/55009)) ([9309aec](9309aec209))
* validate company region in uae vat 201 (backport [#54899](https://github.com/frappe/erpnext/issues/54899)) ([#55054](https://github.com/frappe/erpnext/issues/55054)) ([5ad80b8](5ad80b8fb9))

### Reverts

* Revert "fix: debit credit not equal in purchase transactions for mult… (backport [#54906](https://github.com/frappe/erpnext/issues/54906)) ([#54907](https://github.com/frappe/erpnext/issues/54907)) ([6d3cd7d](6d3cd7d38a))
2026-05-20 04:10:36 +00:00
diptanilsaha
a4bdbec8f8 Merge pull request #55052 from frappe/version-15-hotfix
chore: release v15
2026-05-20 09:38:58 +05:30
Nabin Hait
0b41df5ac8 fix: item leaderboard uses Sales/Purchase Invoice instead of Orders (#55038)
Fixes https://github.com/frappe/erpnext/issues/46657
2026-05-20 00:49:39 +05:30
ruthra kumar
08f4437902 Merge pull request #55056 from frappe/mergify/bp/version-15-hotfix/pr-55053
fix(patch): drop dead procedures first before other changes (backport #55053)
2026-05-19 17:01:32 +05:30
mergify[bot]
5ad80b8fb9 fix: validate company region in uae vat 201 (backport #54899) (#55054)
Co-authored-by: Ravibharathi <131471282+ravibharathi656@users.noreply.github.com>
fix: validate company region in uae vat 201 (#54899)
2026-05-19 16:51:59 +05:30
ruthra kumar
67d67616ca fix(patch): drop dead procedures first before other changes
(cherry picked from commit 61d24ba55f)
2026-05-19 11:08:24 +00:00
rohitwaghchaure
1983204112 Merge pull request #55047 from frappe/mergify/bp/version-15-hotfix/pr-55046
fix: stock balance showing incorrect value because of incorrect SLE (backport #55046)
2026-05-19 14:14:47 +05:30
Rohit Waghchaure
dbacfd13b8 fix: stock balance showing incorrect value because of incorrect SLE
(cherry picked from commit 94b95d6c2f)
2026-05-19 08:22:09 +00:00
Ravibharathi
3b3e33d354 Merge pull request #55041 from frappe/mergify/bp/version-15-hotfix/pr-54761
fix: normalize date comparison to avoid datatype mismatch (backport #54761)
2026-05-19 11:53:32 +05:30
ervishnucs
49b4830785 fix: normalize date comparison to avoid datatype mismatch
(cherry picked from commit 01e382b106)

# Conflicts:
#	erpnext/accounts/party.py
2026-05-19 11:38:21 +05:30
mergify[bot]
5e1880f09e fix(stock): update buying amount calculation in gross profit report (backport #55020) (#55023)
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
fix(stock): update buying amount calculation in gross profit report (#55020)
2026-05-19 09:44:37 +05:30
ruthra kumar
470bf628c7 Merge pull request #55014 from frappe/mergify/bp/version-15-hotfix/pr-55001
fix: remove sql procedure method from AR report (backport #55001)
2026-05-18 14:05:49 +05:30
ruthra kumar
c705a93776 fix: remove sql procedure method from AR report
(cherry picked from commit 63a7142b9b)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.py
#	erpnext/accounts/report/accounts_receivable/accounts_receivable.py
#	erpnext/patches.txt
2026-05-18 13:49:25 +05:30
Nishka Gosalia
9ef783cd48 Merge pull request #55010 from frappe/mergify/bp/version-15-hotfix/pr-55009
fix: toast message for item price insert (backport #55009)
2026-05-18 12:04:48 +05:30
Nishka Gosalia
8512eb4493 fix: merge conflicts 2026-05-18 11:44:57 +05:30
Nishka Gosalia
9309aec209 fix: toast message for item price insert (#55009)
(cherry picked from commit ae9c632e39)

# Conflicts:
#	erpnext/stock/get_item_details.py
2026-05-18 06:11:11 +00:00
ruthra kumar
d02314935d Merge pull request #55003 from frappe/mergify/bp/version-15-hotfix/pr-51197
refactor: remove custom sql function in AR SQL procedure approach (backport #51197)
2026-05-18 11:11:54 +05:30
ruthra kumar
f5d83599cc refactor: remove custom sql function in AR SQL procedure approach
(cherry picked from commit d1e0992253)

# Conflicts:
#	erpnext/accounts/report/accounts_receivable/accounts_receivable.py
2026-05-18 10:55:04 +05:30
Rohit Waghchaure
6e6ef83d60 fix: incoming rate for legacy serial no
(cherry picked from commit 2773b7c002)

# Conflicts:
#	erpnext/stock/deprecated_serial_batch.py
#	erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
#	erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py
2026-05-15 16:40:08 +00:00
mergify[bot]
651af67b26 fix(payment_entry): fix paid/received amount calculation for multi-currency accounts (backport #54963) (#54969)
* fix(payment_entry): `paid_amount` and `received_amount` calculation depending upon `account_currency`

(cherry picked from commit 69642860ee)

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

* chore: resolve conflicts

---------

Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-05-15 10:33:07 +00:00
ruthra kumar
47c6bc4b91 Merge pull request #54959 from frappe/mergify/bp/version-15/pr-54941
fix: flag to disable opening balance calculation in general ledger (backport #54941)
2026-05-15 13:43:24 +05:30
ruthra kumar
f037ee6501 refactor: flag to disable opening balance calculation
(cherry picked from commit 28a2230d02)
2026-05-15 07:32:24 +00:00
ruthra kumar
2b2eb2fa27 Merge pull request #54956 from frappe/mergify/bp/version-15-hotfix/pr-54941
fix: flag to disable opening balance calculation in general ledger (backport #54941)
2026-05-15 13:01:01 +05:30
ruthra kumar
d1d4480187 refactor: flag to disable opening balance calculation
(cherry picked from commit 28a2230d02)
2026-05-15 06:50:58 +00:00
mergify[bot]
48ed07816d fix(stock): add whole number quantity validation in Stock Reconciliation (backport #54922) (#54924)
fix(stock): add whole number quantity validation in Stock Reconciliation (#54922)

(cherry picked from commit f9dec73042)

Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
2026-05-13 15:20:13 +00:00
Frappe PR Bot
d43862624a chore(release): Bumped to Version 15.108.1
## [15.108.1](https://github.com/frappe/erpnext/compare/v15.108.0...v15.108.1) (2026-05-13)

### Reverts

* Revert "fix: debit credit not equal in purchase transactions for mult… (backport [#54906](https://github.com/frappe/erpnext/issues/54906)) (backport [#54907](https://github.com/frappe/erpnext/issues/54907)) ([#54917](https://github.com/frappe/erpnext/issues/54917)) ([dc4b9cc](dc4b9cc4bc))
2026-05-13 11:16:09 +00:00
mergify[bot]
dc4b9cc4bc Revert "fix: debit credit not equal in purchase transactions for mult… (backport #54906) (backport #54907) (#54917)
Revert "fix: debit credit not equal in purchase transactions for mult… (backport #54906) (#54907)

* Revert "fix: debit credit not equal in purchase transactions for mult… (#54906)

* Revert "fix: debit credit not equal in purchase transactions for multi currency"

This reverts commit 75bcea57f4.

* Revert "test: add test case"

This reverts commit 1d30a202c3.

* Revert "fix: include rejected qty in tax (purchase receipt)"

This reverts commit 8c9a88abbe.

(cherry picked from commit cf5e8ce878)

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

* chore: resolve conflicts

---------


(cherry picked from commit 6d3cd7d38a)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-13 16:44:53 +05:30
mergify[bot]
6d3cd7d38a Revert "fix: debit credit not equal in purchase transactions for mult… (backport #54906) (#54907)
* Revert "fix: debit credit not equal in purchase transactions for mult… (#54906)

* Revert "fix: debit credit not equal in purchase transactions for multi currency"

This reverts commit 75bcea57f4.

* Revert "test: add test case"

This reverts commit 1d30a202c3.

* Revert "fix: include rejected qty in tax (purchase receipt)"

This reverts commit 8c9a88abbe.

(cherry picked from commit cf5e8ce878)

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

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-13 09:58:45 +00:00
mergify[bot]
b969662b6c fix: add warehouse vaildation for repack entry (backport #54866) (#54900)
* fix: add warehouse vaildation for repack entry (#54866)

(cherry picked from commit bc07b2d3e5)

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

* chore: resolve conflicts

---------

Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-13 06:42:18 +00:00
Frappe PR Bot
52d6b72a6b chore(release): Bumped to Version 15.108.0
# [15.108.0](https://github.com/frappe/erpnext/compare/v15.107.0...v15.108.0) (2026-05-12)

### Bug Fixes

* added permission validation for `deactivate_sales_person` (backport [#54884](https://github.com/frappe/erpnext/issues/54884)) ([#54885](https://github.com/frappe/erpnext/issues/54885)) ([9586bc7](9586bc7635))
* correct payment request function call in si and so ([603700a](603700aa0e))
* **crm:** handle empty _assign in appointment auto assignment (backport [#54782](https://github.com/frappe/erpnext/issues/54782)) ([#54794](https://github.com/frappe/erpnext/issues/54794)) ([6eaf92a](6eaf92aae6))
* decimal issue ([a5ff2ba](a5ff2bafe0))
* fetch get_item_tax_template while update items ([#54784](https://github.com/frappe/erpnext/issues/54784)) ([455bfcd](455bfcd750))
* fetch hour rate from workstation when operation hour_rate is mis… ([#54820](https://github.com/frappe/erpnext/issues/54820)) ([d57ec6c](d57ec6c094))
* incorrect serial nos picked during disassemble (backport [#54757](https://github.com/frappe/erpnext/issues/54757)) ([#54759](https://github.com/frappe/erpnext/issues/54759)) ([1e2a719](1e2a7196e5))
* incorrect validation thrown for drop shipped PI (backport [#54751](https://github.com/frappe/erpnext/issues/54751)) ([#54752](https://github.com/frappe/erpnext/issues/54752)) ([da95f83](da95f83686))
* raw material should not have target warehouse in manufacture entry (backport [#54849](https://github.com/frappe/erpnext/issues/54849)) ([#54860](https://github.com/frappe/erpnext/issues/54860)) ([bad85ad](bad85ad01b))
* **stock:** apply filters for rejected warehouse in pick list (backport [#54733](https://github.com/frappe/erpnext/issues/54733)) ([#54775](https://github.com/frappe/erpnext/issues/54775)) ([e5a6b5b](e5a6b5b3a0))
* **stock:** ignore reserved qty for stock levels in batch (backport [#54790](https://github.com/frappe/erpnext/issues/54790)) ([#54796](https://github.com/frappe/erpnext/issues/54796)) ([c3ac7aa](c3ac7aac66))
* **stock:** priorities pick list parent warehouse (backport [#54788](https://github.com/frappe/erpnext/issues/54788)) ([#54792](https://github.com/frappe/erpnext/issues/54792)) ([c3467cc](c3467cc169))
* **task:** update depends_on for closing date and review date [#54850](https://github.com/frappe/erpnext/issues/54850) (backport [#54852](https://github.com/frappe/erpnext/issues/54852)) ([#54862](https://github.com/frappe/erpnext/issues/54862)) ([213342a](213342a37c))
* validate variant values (backport [#54831](https://github.com/frappe/erpnext/issues/54831)) ([#54838](https://github.com/frappe/erpnext/issues/54838)) ([910fe9e](910fe9ef55))

### Features

* Philippines chart of account (backport [#53918](https://github.com/frappe/erpnext/issues/53918)) ([#54887](https://github.com/frappe/erpnext/issues/54887)) ([e9cfb04](e9cfb046a1))
2026-05-12 18:49:28 +00:00
diptanilsaha
594b5a2729 Merge pull request #54864 from frappe/version-15-hotfix
chore: release v15
2026-05-13 00:18:10 +05:30
mergify[bot]
e9cfb046a1 feat: Philippines chart of account (backport #53918) (#54887)
feat: Added Philippines chart of account json file (#53918)

* feat: Added philipinnes chart of account json file



* feat: made changes as per review comments and corrected indentation

* feat: made changes as per review comments

* feat: made changes as per review comments to resolve the issues

* fix: fixed changes as per review comments



* fix: fixed changes as per review comments on bank group account



---------




(cherry picked from commit 5560f6c270)

Signed-off-by: Soham-ambibuzz <soham.pawar@ambibuzz.com>
Signed-off-by: soham7117 <sohampawar626@gmail.com>
Co-authored-by: Soham-ambibuzz <soham.pawar@ambibuzz.com>
Co-authored-by: soham7117 <sohampawar626@gmail.com>
2026-05-12 16:40:56 +00:00
mergify[bot]
9586bc7635 fix: added permission validation for deactivate_sales_person (backport #54884) (#54885)
* fix: added permission validation for `deactivate_sales_person` (#54884)

(cherry picked from commit 9134db9cd3)

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

* chore: resolved conflicts

---------

Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-05-12 16:30:56 +00:00
mergify[bot]
213342a37c fix(task): update depends_on for closing date and review date #54850 (backport #54852) (#54862)
fix(task): update depends_on for closing date and review date #54850 (#54852)

(cherry picked from commit 3532c1cc69)

Co-authored-by: Jaypal Lakum <96212547+jp-the-dev@users.noreply.github.com>
2026-05-12 10:13:10 +00:00
mergify[bot]
bad85ad01b fix: raw material should not have target warehouse in manufacture entry (backport #54849) (#54860)
* fix: raw material should not have target warehouse in manufacture entry (#54849)

(cherry picked from commit b5527cf328)

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

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-12 09:46:54 +00:00
Ravibharathi
684f072bca Merge pull request #54857 from aerele/v15-rename-payment-request-call
fix: correct payment request function call in si and so
2026-05-12 13:58:34 +05:30
sudarsan2001
603700aa0e fix: correct payment request function call in si and so 2026-05-12 13:47:32 +05:30
mergify[bot]
910fe9ef55 fix: validate variant values (backport #54831) (#54838)
fix: validate variant values (#54831)

(cherry picked from commit 95705f18aa)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-11 21:23:24 +05:30
Pandiyan P
d57ec6c094 fix: fetch hour rate from workstation when operation hour_rate is mis… (#54820)
fix: fetch hour rate from workstation when operation hour_rate is missing
2026-05-11 13:18:51 +05:30
mergify[bot]
6eaf92aae6 fix(crm): handle empty _assign in appointment auto assignment (backport #54782) (#54794)
fix(crm): handle empty _assign in appointment auto assignment (#54782)

(cherry picked from commit a4a389bd41)

Co-authored-by: Sakthivel Murugan S <129778327+ssakthivelmurugan@users.noreply.github.com>
2026-05-08 12:48:15 +00:00
Ravibharathi
455bfcd750 fix: fetch get_item_tax_template while update items (#54784) 2026-05-08 12:47:33 +00:00
mergify[bot]
c3ac7aac66 fix(stock): ignore reserved qty for stock levels in batch (backport #54790) (#54796)
fix(stock): ignore reserved qty for stock levels in batch (#54790)

(cherry picked from commit 0b6a372a52)

Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
2026-05-08 12:39:14 +00:00
mergify[bot]
c3467cc169 fix(stock): priorities pick list parent warehouse (backport #54788) (#54792)
fix(stock): priorities pick list parent warehouse (#54788)

(cherry picked from commit 4e850f31d5)

Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
2026-05-08 12:34:59 +00:00
mergify[bot]
e5a6b5b3a0 fix(stock): apply filters for rejected warehouse in pick list (backport #54733) (#54775)
fix(stock): apply filters for rejected warehouse in pick list (#54733)

(cherry picked from commit 0fc96e8f7d)

Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
2026-05-07 16:00:22 +05:30
mergify[bot]
1e2a7196e5 fix: incorrect serial nos picked during disassemble (backport #54757) (#54759)
fix: incorrect serial nos picked during disassemble

(cherry picked from commit 25f7fa548d)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2026-05-06 16:05:50 +05:30
mergify[bot]
da95f83686 fix: incorrect validation thrown for drop shipped PI (backport #54751) (#54752)
* fix: incorrect validation thrown for drop shipped PI (#54751)

(cherry picked from commit 907a809f3f)

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

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-06 05:53:11 +00:00
rohitwaghchaure
84aa8e5c9f Merge pull request #54744 from frappe/mergify/bp/version-15-hotfix/pr-54723
fix: decimal issue in stock ageing report (backport #54723)
2026-05-05 22:02:43 +05:30
Frappe PR Bot
fc54fd09f1 chore(release): Bumped to Version 15.107.0
# [15.107.0](https://github.com/frappe/erpnext/compare/v15.106.0...v15.107.0) (2026-05-05)

### Bug Fixes

* accounts and account types in German CoA "SKR 03" ([#54711](https://github.com/frappe/erpnext/issues/54711)) ([581529f](581529fd00))
* copy project from first row to new rows (backport [#53295](https://github.com/frappe/erpnext/issues/53295)) ([#54619](https://github.com/frappe/erpnext/issues/54619)) ([698b087](698b087997))
* correct project filter in buying doctypes (backport [#54644](https://github.com/frappe/erpnext/issues/54644)) ([#54651](https://github.com/frappe/erpnext/issues/54651)) ([329f4e0](329f4e01a3))
* dont show serial/batch button when PR is submitted (backport [#54642](https://github.com/frappe/erpnext/issues/54642)) ([#54645](https://github.com/frappe/erpnext/issues/54645)) ([1b1bc3d](1b1bc3d81c))
* error when creating quotation from CRM (backport [#54722](https://github.com/frappe/erpnext/issues/54722)) ([#54724](https://github.com/frappe/erpnext/issues/54724)) ([1a406e9](1a406e90c1))
* error when creating quotation from CRM (backport [#54722](https://github.com/frappe/erpnext/issues/54722)) ([#54724](https://github.com/frappe/erpnext/issues/54724)) ([809feb9](809feb9c04))
* hide payment and payment request buttons based on permissions in invoices and orders (backport [#53920](https://github.com/frappe/erpnext/issues/53920)) ([#54735](https://github.com/frappe/erpnext/issues/54735)) ([9c9ecc7](9c9ecc77f8))
* incorrect expense account book in purchase return (backport [#54681](https://github.com/frappe/erpnext/issues/54681)) ([#54692](https://github.com/frappe/erpnext/issues/54692)) ([a3bb409](a3bb40904c))
* item query in quality inspection ([#54721](https://github.com/frappe/erpnext/issues/54721)) ([0b0f9d0](0b0f9d046d))
* **payment_entry:** convert the date args to string type before escaping in `get_outstanding_reference_documents` (backport [#54639](https://github.com/frappe/erpnext/issues/54639)) ([#54647](https://github.com/frappe/erpnext/issues/54647)) ([4bab1e4](4bab1e4142))
* **project:** use user.email for invitations and skip disabled users. (backport [#54561](https://github.com/frappe/erpnext/issues/54561)) ([#54666](https://github.com/frappe/erpnext/issues/54666)) ([58d95a3](58d95a35ff))
* **selling:** blanket order ordered qty recalculation on sales order status change (backport [#54593](https://github.com/frappe/erpnext/issues/54593)) ([#54622](https://github.com/frappe/erpnext/issues/54622)) ([d64b194](d64b19416e))
* set valid_from in created Item Price ([#54696](https://github.com/frappe/erpnext/issues/54696)) ([6246a9a](6246a9aa6e))
* show correct status in Serial No Ledger (backport [#54567](https://github.com/frappe/erpnext/issues/54567)) ([#54625](https://github.com/frappe/erpnext/issues/54625)) ([559b31b](559b31baae))
* show in and out qty in the stock ledger report for stock recos ([393fe75](393fe75363))
* use RecoverableErrors isinstance check for repost timeout status ([a49e2de](a49e2de866))

### Features

* copy terms attachments to transactions (backport [#53403](https://github.com/frappe/erpnext/issues/53403)) ([#54660](https://github.com/frappe/erpnext/issues/54660)) ([29282a8](29282a80cf))
2026-05-05 16:32:38 +00:00
diptanilsaha
31bf9bd1fd Merge pull request #54741 from frappe/version-15-hotfix 2026-05-05 22:00:21 +05:30
Rohit Waghchaure
a5ff2bafe0 fix: decimal issue
(cherry picked from commit 542eb6aca4)
2026-05-05 11:12:50 +00:00
mergify[bot]
9c9ecc77f8 fix: hide payment and payment request buttons based on permissions in invoices and orders (backport #53920) (#54735)
Co-authored-by: ravibharathi656 <ravibharathi656@gmail.com>
Co-authored-by: Sakthivel Murugan S <129778327+ssakthivelmurugan@users.noreply.github.com>
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix: hide payment and payment request buttons based on permissions in invoices and orders (#53920)
2026-05-05 12:25:18 +05:30
mergify[bot]
1a406e90c1 fix: error when creating quotation from CRM (backport #54722) (#54724)
fix: error when creating quotation from CRM (#54722)

(cherry picked from commit 2d3190effb)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-04 21:30:11 +05:30
mergify[bot]
809feb9c04 fix: error when creating quotation from CRM (backport #54722) (#54724)
fix: error when creating quotation from CRM (#54722)

(cherry picked from commit 2d3190effb)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-05-04 21:30:02 +05:30
Mihir Kandoi
0b0f9d046d fix: item query in quality inspection (#54721) 2026-05-04 15:31:10 +00:00
mergify[bot]
d07d7feb3f refactor: Sales Partner Commission Summary and Sales Partner Transaction Summary report (backport #54268) (#54430)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-05-04 11:22:32 +05:30
Raffael Meyer
581529fd00 fix: accounts and account types in German CoA "SKR 03" (#54711) 2026-05-03 17:25:59 +00:00
Kaajalchhattani
6246a9aa6e fix: set valid_from in created Item Price (#54696)
Co-authored-by: Kaajal-Chhattani <kaajal.chhattani@aurigait.com>
2026-05-02 21:23:03 +05:30
mergify[bot]
29282a80cf feat: copy terms attachments to transactions (backport #53403) (#54660)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2026-05-01 13:04:07 +00:00
mergify[bot]
a3bb40904c fix: incorrect expense account book in purchase return (backport #54681) (#54692)
fix: incorrect expense account book in purchase return

(cherry picked from commit 2a720e7008)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2026-05-01 12:46:56 +05:30
Raffael Meyer
3919c3d385 refactor: re-save Item Tax Template (#54688) 2026-04-30 22:07:13 +00:00
mergify[bot]
58d95a35ff fix(project): use user.email for invitations and skip disabled users. (backport #54561) (#54666)
fix(project): use user.email for invitations and skip disabled users. (#54561)

* fix(project): use user.email for invitations and skip disabled users.

* Update erpnext/projects/doctype/project/project.py



* fix(project): remove duplicate loop causing indentation error

* fix(project): resolve pre-commit hook failure

---------


(cherry picked from commit 231dd1856f)

Co-authored-by: Hemil-Sangani <hemil@sanskartechnolab.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-04-30 14:35:29 +05:30
rohitwaghchaure
4bb450c294 Merge pull request #54670 from frappe/mergify/bp/version-15-hotfix/pr-54664
fix: show in and out qty in the stock ledger report for stock recos (backport #54664)
2026-04-30 14:33:42 +05:30
Rohit Waghchaure
393fe75363 fix: show in and out qty in the stock ledger report for stock recos
(cherry picked from commit da081254a6)
2026-04-30 08:44:10 +00:00
mergify[bot]
329f4e01a3 fix: correct project filter in buying doctypes (backport #54644) (#54651)
fix: correct project filter in buying doctypes (#54644)

(cherry picked from commit a04c028522)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-29 17:28:02 +05:30
mergify[bot]
4bab1e4142 fix(payment_entry): convert the date args to string type before escaping in get_outstanding_reference_documents (backport #54639) (#54647)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(payment_entry): convert the date args to string type before escaping in `get_outstanding_reference_documents` (#54639)
2026-04-29 11:35:49 +00:00
mergify[bot]
1b1bc3d81c fix: dont show serial/batch button when PR is submitted (backport #54642) (#54645)
* fix: dont show serial/batch button when PR is submitted (#54642)

(cherry picked from commit 060defcc2b)

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

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-29 11:25:06 +00:00
rohitwaghchaure
88588769f1 Merge pull request #54543 from AssemBahnasy/fix/repost-recoverable-errors-status
fix: use RecoverableErrors isinstance check for repost timeout status
2026-04-29 16:50:55 +05:30
mergify[bot]
559b31baae fix: show correct status in Serial No Ledger (backport #54567) (#54625)
* refactor: extract SN status logic

(cherry picked from commit cb2e6e1e2e)

* fix: show correct status in Serial No Ledger

(cherry picked from commit 2b3e047143)

---------

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2026-04-29 13:55:06 +05:30
mergify[bot]
d64b19416e fix(selling): blanket order ordered qty recalculation on sales order status change (backport #54593) (#54622)
fix(selling): blanket order ordered qty recalculation on sales order status change (#54593)

(cherry picked from commit d68801e73a)

Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
2026-04-29 06:45:18 +00:00
mergify[bot]
698b087997 fix: copy project from first row to new rows (backport #53295) (#54619)
fix: copy project to new item row from parent

(cherry picked from commit 68cc518497)

Co-authored-by: ravibharathi656 <ravibharathi656@gmail.com>
2026-04-29 11:55:36 +05:30
Frappe PR Bot
4dd9f0b255 chore(release): Bumped to Version 15.106.0
# [15.106.0](https://github.com/frappe/erpnext/compare/v15.105.0...v15.106.0) (2026-04-28)

### Bug Fixes

* **`get_stock_balance`:** validate inventory dimension fieldnames (backport [#54587](https://github.com/frappe/erpnext/issues/54587)) ([#54588](https://github.com/frappe/erpnext/issues/54588)) ([03f3a28](03f3a28f54))
* **accounts:** fetch project name from payment entry to journal entry ([55cce2a](55cce2a11c))
* add party_type for dynamic link and add it to grouping key ([a3ad1fb](a3ad1fb163))
* add project filter to accounts payable and receivable reports (backport [#54344](https://github.com/frappe/erpnext/issues/54344)) ([#54441](https://github.com/frappe/erpnext/issues/54441)) ([44f3f34](44f3f34c9e))
* avoid double reduction of pe reference outstanding (backport [#54193](https://github.com/frappe/erpnext/issues/54193)) ([#54612](https://github.com/frappe/erpnext/issues/54612)) ([51e7c66](51e7c66043))
* debit credit not equal in purchase transactions for multi currency (backport [#54456](https://github.com/frappe/erpnext/issues/54456)) ([#54563](https://github.com/frappe/erpnext/issues/54563)) ([78b2e45](78b2e45cb9))
* duplicate entries being shown in batch exists in future transact… (backport [#54604](https://github.com/frappe/erpnext/issues/54604)) ([#54605](https://github.com/frappe/erpnext/issues/54605)) ([176d980](176d980764))
* **edi:** restrict Code List imports to files and trusted backend URLs (backport [#54137](https://github.com/frappe/erpnext/issues/54137)) ([#54265](https://github.com/frappe/erpnext/issues/54265)) ([e0013f7](e0013f7618)), closes [#54488](https://github.com/frappe/erpnext/issues/54488)
* negative quantity check in validate_item_qty (backport [#54559](https://github.com/frappe/erpnext/issues/54559)) ([#54571](https://github.com/frappe/erpnext/issues/54571)) ([49ab25d](49ab25dda8))
* **payment_entry:** escape arguments on invoice and order fetching sql queries (backport [#54582](https://github.com/frappe/erpnext/issues/54582)) ([#54585](https://github.com/frappe/erpnext/issues/54585)) ([cceedd6](cceedd669f))
* **PCV:** set correct filters of `from_date` and `to_date` on General Ledger Report on clicking `Ledger` button (backport [#54522](https://github.com/frappe/erpnext/issues/54522)) ([#54523](https://github.com/frappe/erpnext/issues/54523)) ([6df39ae](6df39aec54))
* preserve inventory dimensions when raw materials are reset (backport [#54440](https://github.com/frappe/erpnext/issues/54440)) ([#54492](https://github.com/frappe/erpnext/issues/54492)) ([722dc8c](722dc8c3f1))
* **purchase_register:** filter tax rows by parenttype in invoice tax map query (backport [#54272](https://github.com/frappe/erpnext/issues/54272)) ([#54443](https://github.com/frappe/erpnext/issues/54443)) ([4dff436](4dff436104))
* py error on stock ageing report (backport [#54467](https://github.com/frappe/erpnext/issues/54467)) ([#54468](https://github.com/frappe/erpnext/issues/54468)) ([6179449](6179449036))
* sales order is not valid when creating WO from MR from PP (backport [#54435](https://github.com/frappe/erpnext/issues/54435)) ([#54470](https://github.com/frappe/erpnext/issues/54470)) ([9a4c693](9a4c693f2d))
* **stock:** remove validation for transfer_qty field (backport [#54542](https://github.com/frappe/erpnext/issues/54542)) ([#54544](https://github.com/frappe/erpnext/issues/54544)) ([8569ff6](8569ff67ff))
* **stock:** set incoming rate as zero for outward sle (backport [#54514](https://github.com/frappe/erpnext/issues/54514)) ([#54532](https://github.com/frappe/erpnext/issues/54532)) ([68d213a](68d213a244))
* unknown column error on item code in quality inspection ([#54565](https://github.com/frappe/erpnext/issues/54565)) ([e7a29ab](e7a29abdb0))
* update status of quotation in patch (backport [#54577](https://github.com/frappe/erpnext/issues/54577)) ([#54579](https://github.com/frappe/erpnext/issues/54579)) ([1a8dc7e](1a8dc7e332))
* use key consistently ([8f9a5e6](8f9a5e6c0c))

### Features

* danish_bosnian_address_template (backport [#54093](https://github.com/frappe/erpnext/issues/54093)) ([#54515](https://github.com/frappe/erpnext/issues/54515)) ([973444e](973444e20e))

### Reverts

* Revert "fix: preserve inventory dimensions when raw materials are reset (backport [#54440](https://github.com/frappe/erpnext/issues/54440))" ([#54507](https://github.com/frappe/erpnext/issues/54507)) ([1b08ac2](1b08ac248b))
* Revert "refactor: quality inspection item query (backport [#54511](https://github.com/frappe/erpnext/issues/54511))" ([#54557](https://github.com/frappe/erpnext/issues/54557)) ([f869e86](f869e86c9c)), closes [#54539](https://github.com/frappe/erpnext/issues/54539)
2026-04-28 21:00:34 +00:00
diptanilsaha
54b9392cc5 Merge pull request #54584 from frappe/version-15-hotfix 2026-04-29 02:28:40 +05:30
mergify[bot]
51e7c66043 fix: avoid double reduction of pe reference outstanding (backport #54193) (#54612)
* fix: avoid double reduction of pe reference outstanding (#54193)

Co-authored-by: diptanilsaha <diptanil@frappe.io>
(cherry picked from commit d1a80d40c4)

# Conflicts:
#	erpnext/accounts/utils.py

* chore: resolved conflict

* chore: remove unused import of DateTimeLikeObject

---------

Co-authored-by: Ravibharathi <131471282+ravibharathi656@users.noreply.github.com>
Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-04-28 20:35:46 +00:00
mergify[bot]
44f3f34c9e fix: add project filter to accounts payable and receivable reports (backport #54344) (#54441)
Co-authored-by: ljain112 <ljain112@gmail.com>
2026-04-28 22:39:42 +05:30
mergify[bot]
176d980764 fix: duplicate entries being shown in batch exists in future transact… (backport #54604) (#54605)
fix: duplicate entries being shown in batch exists in future transact… (#54604)

fix: duplicate entries being shown in batch exists in future transactions msg
(cherry picked from commit 54f20de7e3)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-28 22:23:03 +05:30
mergify[bot]
44af175556 refactor(sms_center): replaced raw SQL queries with Query Builder (backport #54600) (#54602)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-04-28 15:31:58 +00:00
mergify[bot]
03f3a28f54 fix(get_stock_balance): validate inventory dimension fieldnames (backport #54587) (#54588)
* fix(`get_stock_balance`): validate inventory dimension fieldnames (#54587)

(cherry picked from commit 084c7f72f0)

# Conflicts:
#	erpnext/stock/utils.py

* chore: resolved conflicts

---------

Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-04-28 18:14:07 +05:30
mergify[bot]
cceedd669f fix(payment_entry): escape arguments on invoice and order fetching sql queries (backport #54582) (#54585)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(payment_entry): escape arguments on invoice and order fetching sql queries (#54582)
2026-04-28 10:44:39 +00:00
mergify[bot]
1a8dc7e332 fix: update status of quotation in patch (backport #54577) (#54579)
fix: update status of quotation in patch (#54577)

(cherry picked from commit 2088a01c19)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-28 15:13:16 +05:30
mergify[bot]
49ab25dda8 fix: negative quantity check in validate_item_qty (backport #54559) (#54571)
fix: negative quantity check in validate_item_qty (#54559)

Fix negative quantity check in validate_item_qty

When saving a Blanket Order with a blank qty field in the items table, the following error is raised:

TypeError: '<' not supported between instances of 'NoneType' and 'int'

Root cause: The validate_item_qty method compares d.qty < 0 directly. When the qty field is left empty, its value is None, and Python cannot compare None with an integer.

Fix
Wrap d.qty with flt(), which safely converts None (and any non-numeric value) to 0.0 before the comparison.

# Before
if d.qty < 0:

# After
if flt(d.qty) < 0:

(cherry picked from commit 63edd5ddc6)

Co-authored-by: Vinay Mishra <39999379+vinaymishraofficial@users.noreply.github.com>
2026-04-28 05:30:26 +00:00
Mihir Kandoi
e7a29abdb0 fix: unknown column error on item code in quality inspection (#54565) 2026-04-28 10:19:43 +05:30
mergify[bot]
78b2e45cb9 fix: debit credit not equal in purchase transactions for multi currency (backport #54456) (#54563)
fix: debit credit not equal in purchase transactions for multi currency (#54456)

(cherry picked from commit 601581d6f8)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-27 15:14:58 +00:00
mergify[bot]
4dff436104 fix(purchase_register): filter tax rows by parenttype in invoice tax map query (backport #54272) (#54443)
Co-authored-by: ljain112 <ljain112@gmail.com>
2026-04-27 18:39:01 +05:30
Mihir Kandoi
f869e86c9c Revert "refactor: quality inspection item query (backport #54511)" (#54557)
Revert "refactor: quality inspection item query (backport #54511) (#54539)"

This reverts commit b01049814a.
2026-04-27 10:15:45 +00:00
mergify[bot]
8569ff67ff fix(stock): remove validation for transfer_qty field (backport #54542) (#54544)
fix(stock): remove validation for transfer_qty field (#54542)

(cherry picked from commit 60a6b38c31)

Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
2026-04-27 07:12:24 +00:00
Assem Bahnasy
a49e2de866 fix: use RecoverableErrors isinstance check for repost timeout status
When a Repost Item Valuation job is killed by an RQ worker timeout
(JobTimeoutException raised via SIGALRM), the existing status detection
relied solely on traceback string matching for 'timeout' or 'Deadlock'.

This is unreliable because SIGALRM can interrupt a C-extension call
(e.g. inside pypika's copy.copy()) before Python records the exception
in the traceback. In that case the traceback shows only the interrupted
frame -- not JobTimeoutException -- so the job is permanently marked
'Failed' instead of 'In Progress', preventing the scheduler from
automatically retrying it.

RecoverableErrors = (JobTimeoutException, QueryDeadlockError,
QueryTimeoutError) is already defined at the top of this file and is
already used further down in the same except block to suppress email
notifications. Extend its use to also guard the status decision.

The traceback string fallback is kept as a secondary check for
forward compatibility with other timeout signals.

Fixes: jobs permanently stuck as 'Failed' after RQ worker timeout,
requiring manual re-queue to resume reposting.
2026-04-27 07:05:35 +00:00
mergify[bot]
b01049814a refactor: quality inspection item query (backport #54511) (#54539)
* refactor: quality inspection item query (#54511)

(cherry picked from commit be2a4b7b2a)

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

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-27 05:51:18 +00:00
mergify[bot]
973444e20e feat: danish_bosnian_address_template (backport #54093) (#54515)
feat: danish_bosnian_address_template (#54093)

(cherry picked from commit e517eeaaa2)

Co-authored-by: mahsem <137205921+mahsem@users.noreply.github.com>
2026-04-26 21:06:37 +05:30
mergify[bot]
68d213a244 fix(stock): set incoming rate as zero for outward sle (backport #54514) (#54532)
fix(stock): set incoming rate as zero for outward sle

(cherry picked from commit ce37530e70)

Co-authored-by: Sudharsanan11 <sudharsananashok1975@gmail.com>
2026-04-26 20:24:43 +05:30
mergify[bot]
6df39aec54 fix(PCV): set correct filters of from_date and to_date on General Ledger Report on clicking Ledger button (backport #54522) (#54523)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(PCV): set correct filters of `from_date` and `to_date` on General Ledger Report on clicking `Ledger` button (#54522)
2026-04-25 00:06:32 +05:30
mergify[bot]
071a28ff8c refactor: use consistent report column names (backport #54451) (#54518)
* refactor: use consistent report column names

(cherry picked from commit 7630c01e40)

* refactor: better label for entity type

(cherry picked from commit 8e12bda108)

* fix: add party_type for dynamic link and add it to grouping key

(cherry picked from commit a3ad1fb163)

* fix: use key consistently

(cherry picked from commit 8f9a5e6c0c)

---------

Co-authored-by: Smit Vora <smitvora203@gmail.com>
2026-04-24 14:32:10 +00:00
Mihir Kandoi
1b08ac248b Revert "fix: preserve inventory dimensions when raw materials are reset (backport #54440)" (#54507)
Revert "fix: preserve inventory dimensions when raw materials are reset (back…"

This reverts commit 722dc8c3f1.
2026-04-24 08:43:59 +00:00
mergify[bot]
722dc8c3f1 fix: preserve inventory dimensions when raw materials are reset (backport #54440) (#54492)
* fix: preserve inventory dimensions when raw materials are reset (#54440)

* fix: preserve inventory dimensions when raw materials are reset

* test: add test case

(cherry picked from commit 0e20e35842)

# Conflicts:
#	erpnext/patches.txt
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
#	erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py

* chore: resolve conflicts

* chore: resolve conflicts

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-23 17:27:07 +00:00
mergify[bot]
e0013f7618 fix(edi): restrict Code List imports to files and trusted backend URLs (backport #54137) (#54265)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(edi): restrict Code List imports to files and trusted backend URLs (#54137)
fix(edi): hardcode "Code List" DocType in importer (#54488)
2026-04-23 15:37:39 +00:00
Smit Vora
017635ab04 Merge pull request #54451 from vorasmit/tds-reports-refactor-backport 2026-04-23 15:25:46 +05:30
Smit Vora
8f9a5e6c0c fix: use key consistently 2026-04-23 15:01:46 +05:30
mergify[bot]
9a4c693f2d fix: sales order is not valid when creating WO from MR from PP (backport #54435) (#54470)
fix: sales order is not valid when creating WO from MR from PP (#54435)

(cherry picked from commit e65b9fc2ae)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-22 15:05:29 +00:00
mergify[bot]
6179449036 fix: py error on stock ageing report (backport #54467) (#54468)
fix: py error on stock ageing report (#54467)

(cherry picked from commit f5357c233d)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-22 14:32:21 +00:00
Smit Vora
a3ad1fb163 fix: add party_type for dynamic link and add it to grouping key 2026-04-22 12:12:18 +05:30
Smit Vora
8e12bda108 refactor: better label for entity type 2026-04-22 12:11:04 +05:30
Ravibharathi
947b282e0c Merge pull request #54452 from frappe/mergify/bp/version-15-hotfix/pr-54307
fix(accounts): fetch project name from payment entry to journal entry (backport #54307)
2026-04-22 11:24:29 +05:30
sarathibalamurugan
f9ae22d85e test: add test for project name in exchange gain loss entry
(cherry picked from commit 9eeb819106)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/test_payment_entry.py
2026-04-22 11:05:18 +05:30
Frappe PR Bot
b6902ef960 chore(release): Bumped to Version 15.105.0
# [15.105.0](https://github.com/frappe/erpnext/compare/v15.104.3...v15.105.0) (2026-04-21)

### Bug Fixes

* add portal user ownership check to supplier quotation (backport [#54298](https://github.com/frappe/erpnext/issues/54298)) ([#54299](https://github.com/frappe/erpnext/issues/54299)) ([1e4cafa](1e4cafaa0e))
* changed qty validation from qty field to stock_qty (backport [#54352](https://github.com/frappe/erpnext/issues/54352)) ([#54356](https://github.com/frappe/erpnext/issues/54356)) ([1ccbc9f](1ccbc9f621))
* clear conditions table when calculate_based_on is set to Fixed ([35bd437](35bd43775c))
* clear shipping rule conditions for fixed shipping rule ([9e10ecc](9e10ecc4cb))
* **dashboard-trends:** set default fiscal year and company before val… (backport [#54339](https://github.com/frappe/erpnext/issues/54339)) ([#54399](https://github.com/frappe/erpnext/issues/54399)) ([799f897](799f897036))
* fetch item tax template from item group when creating item ([#54405](https://github.com/frappe/erpnext/issues/54405)) ([ffa0268](ffa0268a57))
* move make_dimension_in_accounting_doctypes from after_insert to on_update (backport [#54172](https://github.com/frappe/erpnext/issues/54172)) ([#54317](https://github.com/frappe/erpnext/issues/54317)) ([d9d8fc6](d9d8fc6912))
* negative batch report showing same batch-warehouse multiple times ([3229fce](3229fce9a5))
* non-collapsible in customer quick entry ([9ee0594](9ee059465a))
* **pos_invoice_item:** fetch `grant_commission` from `item_code` (backport [#54413](https://github.com/frappe/erpnext/issues/54413)) ([#54417](https://github.com/frappe/erpnext/issues/54417)) ([813f464](813f4644a0))
* reset base_rounded_total when rounded_total resets (backport [#54241](https://github.com/frappe/erpnext/issues/54241)) ([#54303](https://github.com/frappe/erpnext/issues/54303)) ([28367ac](28367ac966))
* **vat audit report:** fallback to item name when item code is missing ([#54049](https://github.com/frappe/erpnext/issues/54049)) ([2c1ea8d](2c1ea8d30c))

### Features

* enhance tax withholding details report with additional columns support (backport [#54409](https://github.com/frappe/erpnext/issues/54409)) ([#54432](https://github.com/frappe/erpnext/issues/54432)) ([e223260](e22326065d))
2026-04-21 19:53:50 +00:00
diptanilsaha
489ff20021 Merge pull request #54438 from frappe/version-15-hotfix 2026-04-22 01:22:15 +05:30
diptanilsaha
bd957a9bbc Revert "feat: enhance tax withholding details report with additional columns support (backport #54409)" (#54458) 2026-04-21 18:49:08 +00:00
Lakshit Jain
e22326065d feat: enhance tax withholding details report with additional columns support (backport #54409) (#54432) 2026-04-22 00:02:19 +05:30
sarathibalamurugan
55cce2a11c fix(accounts): fetch project name from payment entry to journal entry
(cherry picked from commit d9b255b952)
2026-04-21 13:29:16 +00:00
Smit Vora
7630c01e40 refactor: use consistent report column names 2026-04-21 18:53:25 +05:30
Ravibharathi
bd4eb71205 Merge pull request #54423 from frappe/mergify/bp/version-15-hotfix/pr-54415
fix: clear conditions table when calculate_based_on is set to Fixed (backport #54415)
2026-04-20 19:44:36 +05:30
ravibharathi656
9e10ecc4cb fix: clear shipping rule conditions for fixed shipping rule
(cherry picked from commit d6bb0ae093)
2026-04-20 13:53:03 +00:00
sarathibalamurugan
35bd43775c fix: clear conditions table when calculate_based_on is set to Fixed
(cherry picked from commit d73920be12)
2026-04-20 13:53:02 +00:00
mergify[bot]
813f4644a0 fix(pos_invoice_item): fetch grant_commission from item_code (backport #54413) (#54417)
* fix(pos_invoice_item): fetch `grant_commission` from `item_code` (#54413)

(cherry picked from commit 6c51e4cd1f)

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

* chore: resolve conflicts

---------

Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-04-20 11:48:06 +00:00
Ravibharathi
2c1ea8d30c fix(vat audit report): fallback to item name when item code is missing (#54049)
* fix(vat audit report): fallback to item name when item code is missing

* fix: validate south africa company selection

* fix: simplify parent item lookup

* fix: handle missing item mapping

* fix: use list instead of set
2026-04-20 15:56:37 +05:30
Pandiyan P
ffa0268a57 fix: fetch item tax template from item group when creating item (#54405) 2026-04-20 11:58:16 +05:30
mergify[bot]
1ccbc9f621 fix: changed qty validation from qty field to stock_qty (backport #54352) (#54356)
fix: changed qty validation from qty field to stock_qty (#54352)

(cherry picked from commit ba01d66c24)

Co-authored-by: Jatin3128 <140256508+Jatin3128@users.noreply.github.com>
2026-04-20 10:53:44 +05:30
mergify[bot]
799f897036 fix(dashboard-trends): set default fiscal year and company before val… (backport #54339) (#54399)
* fix(dashboard-trends): set default fiscal year and company before val… (#54339)

* fix(dashboard-trends): set default fiscal year and company before validating filters Ensure  and  are populated with default values

* fix(dashboard-trends): ensure fiscal_year and company are properly set before validation to avoid empty filter issues

* Update erpnext/controllers/trends.py

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
(cherry picked from commit d61b5fd5f6)

# Conflicts:
#	erpnext/controllers/trends.py

* chore: resolve conflicts

---------

Co-authored-by: Ahmed AbuKhatwa <82771130+AhmedAbokhatwa@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-19 19:34:19 +05:30
mergify[bot]
6b7bdfdfd3 Fix : None handling in pricing rule free item quantity calculation (backport #54375) (#54395)
Fix : None handling in pricing rule free item quantity calculation (#54375)

* fix(pricing_rule): handle None qty in transaction_qty calculation

* Update erpnext/accounts/doctype/pricing_rule/utils.py

---------



(cherry picked from commit 82438d6c72)

Co-authored-by: Jaganath-Tridots <jaganath@tridotstech.com>
Co-authored-by: Jagan <jagan@DESKTOP-HPDMQ06.localdomain>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-19 07:57:37 +00:00
rohitwaghchaure
e3374933ed Merge pull request #54359 from frappe/mergify/bp/version-15-hotfix/pr-54354
fix: negative batch report showing same batch-warehouse multiple times (backport #54354)
2026-04-17 21:26:16 +05:30
Rohit Waghchaure
3229fce9a5 fix: negative batch report showing same batch-warehouse multiple times
(cherry picked from commit 700572980d)
2026-04-17 15:41:47 +00:00
mergify[bot]
d9d8fc6912 fix: move make_dimension_in_accounting_doctypes from after_insert to on_update (backport #54172) (#54317)
* fix: move make_dimension_in_accounting_doctypes from after_insert to on_update

(cherry picked from commit ee067e6015)

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

* chore: resolve conflicts in accounting_dimension.py

---------

Co-authored-by: Shllokkk <shllokosan23@gmail.com>
2026-04-17 15:14:17 +05:30
mergify[bot]
28367ac966 fix: reset base_rounded_total when rounded_total resets (backport #54241) (#54303)
* fix: reset base_rounded_total when rounded_total resets

(cherry picked from commit f8d278b733)

# Conflicts:
#	erpnext/controllers/tests/test_taxes_and_totals.py
#	erpnext/public/js/controllers/taxes_and_totals.js

* chore: spelling mistake

(cherry picked from commit e2ac476587)

* chore: resolve conflicts

---------

Co-authored-by: ljain112 <ljain112@gmail.com>
2026-04-16 10:39:57 +05:30
NaviN
67632e81d0 Merge pull request #54308 from frappe/mergify/bp/version-15-hotfix/pr-54306
fix: non-collapsible in customer quick entry (backport #54306)
2026-04-15 17:32:16 +05:30
PKSowmiya05
9ee059465a fix: non-collapsible in customer quick entry
(cherry picked from commit 53e120269d)
2026-04-15 11:56:39 +00:00
mergify[bot]
1e4cafaa0e fix: add portal user ownership check to supplier quotation (backport #54298) (#54299)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: add portal user ownership check to supplier quotation (#54298)
2026-04-15 06:07:23 +00:00
Frappe PR Bot
fc3ceff42f chore(release): Bumped to Version 15.104.3
## [15.104.3](https://github.com/frappe/erpnext/compare/v15.104.2...v15.104.3) (2026-04-14)

### Bug Fixes

* account change in warehouse (backport [#54182](https://github.com/frappe/erpnext/issues/54182)) ([#54204](https://github.com/frappe/erpnext/issues/54204)) ([430705f](430705f56c))
* hardcoded precision causing decimal issues ([7754504](77545042a5))
* inventory dimension patch (backport [#54141](https://github.com/frappe/erpnext/issues/54141)) ([#54145](https://github.com/frappe/erpnext/issues/54145)) ([deb67db](deb67db4a0))
* inventory dimension patch (backport [#54147](https://github.com/frappe/erpnext/issues/54147)) ([#54148](https://github.com/frappe/erpnext/issues/54148)) ([a56d698](a56d6984d1))
* inventory dimensions should not be mandatory unnecesarily (backport [#54064](https://github.com/frappe/erpnext/issues/54064)) ([#54133](https://github.com/frappe/erpnext/issues/54133)) ([a26c845](a26c845332))
* last SLE not updated in the file ([8408e81](8408e81335))
* **list_opportunity_report:** parameterized `lost_reason` ([#54160](https://github.com/frappe/erpnext/issues/54160)) ([1604c21](1604c21602))
* make operation mandatory when any sub operation row is added (backport [#54245](https://github.com/frappe/erpnext/issues/54245)) ([#54247](https://github.com/frappe/erpnext/issues/54247)) ([cbe5ad6](cbe5ad6337))
* preserve asset movement field properties after save ([a87015e](a87015e8e6))
* quality inspection item code fetch perm issue (backport [#54121](https://github.com/frappe/erpnext/issues/54121)) ([#54126](https://github.com/frappe/erpnext/issues/54126)) ([bcd6d99](bcd6d99549))
* remove unneccessary function for serial no status updation (backport [#54191](https://github.com/frappe/erpnext/issues/54191)) ([#54196](https://github.com/frappe/erpnext/issues/54196)) ([cb24d94](cb24d9404d))
* **sales invoice:** toggle Get Items From button based on is_return and POS view (backport [#52594](https://github.com/frappe/erpnext/issues/52594)) ([#54138](https://github.com/frappe/erpnext/issues/54138)) ([5de4102](5de4102dda))
* sanitize genericode import inputs and secure XML parser (backport [#53302](https://github.com/frappe/erpnext/issues/53302)) ([#54174](https://github.com/frappe/erpnext/issues/54174)) ([76e910e](76e910e8c0))
* set default posting time in RIV ([6e438e7](6e438e71eb))
* **stock:** remove float precision to fix precision issue (backport [#54284](https://github.com/frappe/erpnext/issues/54284)) ([#54288](https://github.com/frappe/erpnext/issues/54288)) ([0e9b3b4](0e9b3b459a))
* **stock:** update bin to zero when no previous sle exists (backport [#54236](https://github.com/frappe/erpnext/issues/54236)) ([#54263](https://github.com/frappe/erpnext/issues/54263)) ([46a1c6f](46a1c6fda0))
* update return value in workstation list view indicator (backport [#54198](https://github.com/frappe/erpnext/issues/54198)) ([#54200](https://github.com/frappe/erpnext/issues/54200)) ([0a3f9f0](0a3f9f0b9f))
* update_nsm only in warehouse creation ([#54165](https://github.com/frappe/erpnext/issues/54165)) ([e9c1a09](e9c1a09af3))
2026-04-14 18:20:17 +00:00
diptanilsaha
c74a44e526 Merge pull request #54282 from frappe/version-15-hotfix 2026-04-14 23:48:43 +05:30
mergify[bot]
8b3d65ae78 Revert "fix: sync paid and received amount" (backport #54238) (#54292)
Co-authored-by: Vishnu Priya Baskaran <145791817+ervishnucs@users.noreply.github.com>
fix: sync paid and received amount" (#54238)
2026-04-14 22:27:04 +05:30
mergify[bot]
0e9b3b459a fix(stock): remove float precision to fix precision issue (backport #54284) (#54288)
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
fix(stock): remove float precision to fix precision issue (#54284)
2026-04-14 11:33:23 +00:00
mergify[bot]
46a1c6fda0 fix(stock): update bin to zero when no previous sle exists (backport #54236) (#54263)
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
fix(stock): update bin to zero when no previous sle exists (#54236)
2026-04-13 15:49:45 +00:00
mergify[bot]
cbe5ad6337 fix: make operation mandatory when any sub operation row is added (backport #54245) (#54247)
Co-authored-by: Sudarshan <73628063+sudarsan2001@users.noreply.github.com>
fix: make operation mandatory when any sub operation row is added (#54245)
2026-04-13 21:06:05 +05:30
mergify[bot]
17ce550417 Fix(bom): refetch the rate of item when 'source_from_supplier' is updated (backport #54187) (#54207)
Co-authored-by: Sambhav Saxena <76242518+sambhavsaxena@users.noreply.github.com>
Fix(bom): refetch the rate of item when 'source_from_supplier' is updated (#54187)
2026-04-10 23:44:55 +05:30
mergify[bot]
430705f56c fix: account change in warehouse (backport #54182) (#54204)
Co-authored-by: nishkagosalia <nishka.gosalia@gmail.com>
2026-04-10 20:32:33 +05:30
mergify[bot]
0a3f9f0b9f fix: update return value in workstation list view indicator (backport #54198) (#54200)
Co-authored-by: Praveenkumar Dhanasekar <164200710+Praveenku-mar@users.noreply.github.com>
fix: update return value in workstation list view indicator (#54198)
2026-04-10 16:50:32 +05:30
mergify[bot]
cb24d9404d fix: remove unneccessary function for serial no status updation (backport #54191) (#54196)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: remove unneccessary function for serial no status updation (#54191)
2026-04-10 10:53:04 +00:00
Nishka Gosalia
bc6780d4c7 Merge pull request #54179 from frappe/revert-54170-mergify/bp/version-15-hotfix/pr-54165
fix: update_nsm only in warehouse creation (backport #54165)"
2026-04-09 18:31:00 +05:30
Nishka Gosalia
8b16c310f4 Revert "fix: update_nsm only in warehouse creation (backport #54165)" 2026-04-09 18:12:46 +05:30
mergify[bot]
76e910e8c0 fix: sanitize genericode import inputs and secure XML parser (backport #53302) (#54174)
Co-authored-by: Shllokkk <shllokosan23@gmail.com>
2026-04-09 11:30:24 +00:00
Frappe PR Bot
8aede87290 chore(release): Bumped to Version 15.104.2
## [15.104.2](https://github.com/frappe/erpnext/compare/v15.104.1...v15.104.2) (2026-04-09)

### Bug Fixes

* set default posting time in RIV ([041f99c](041f99c926))
2026-04-09 11:15:07 +00:00
rohitwaghchaure
1b2c7ca21f Merge pull request #54169 from frappe/mergify/bp/version-15/pr-54162
fix: set default posting time in RIV (backport #54161) (backport #54162)
2026-04-09 16:43:37 +05:30
Nishka Gosalia
c44ec7eab4 Merge pull request #54170 from frappe/mergify/bp/version-15-hotfix/pr-54165
fix: update_nsm only in warehouse creation (backport #54165)
2026-04-09 16:29:32 +05:30
Nishka Gosalia
e9c1a09af3 fix: update_nsm only in warehouse creation (#54165)
(cherry picked from commit b0e3fa3979)
2026-04-09 10:28:14 +00:00
rohitwaghchaure
db3a40409f chore: fix conflicts
Removed unused method reset_repost_only_accounting_ledgers and fixed the validate method to set default posting time.

(cherry picked from commit 2df574baae)
2026-04-09 09:58:50 +00:00
Rohit Waghchaure
041f99c926 fix: set default posting time in RIV
(cherry picked from commit a7ece65536)

# Conflicts:
#	erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
(cherry picked from commit 6e438e71eb)
2026-04-09 09:58:50 +00:00
rohitwaghchaure
8843068da9 Merge pull request #54162 from frappe/mergify/bp/version-15-hotfix/pr-54161
fix: set default posting time in RIV (backport #54161)
2026-04-09 15:28:12 +05:30
rohitwaghchaure
2df574baae chore: fix conflicts
Removed unused method reset_repost_only_accounting_ledgers and fixed the validate method to set default posting time.
2026-04-09 14:24:07 +05:30
Rohit Waghchaure
6e438e71eb fix: set default posting time in RIV
(cherry picked from commit a7ece65536)

# Conflicts:
#	erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
2026-04-09 08:26:57 +00:00
diptanilsaha
1604c21602 fix(list_opportunity_report): parameterized lost_reason (#54160) 2026-04-09 07:23:12 +00:00
Aarol D'Souza
97c4cd140b Merge pull request #54157 from frappe/mergify/bp/version-15-hotfix/pr-54129
refactor: update reset password method name (backport #54129)
2026-04-09 12:30:37 +05:30
mergify[bot]
9d64d4ac05 Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr-54129 2026-04-09 06:42:06 +00:00
mergify[bot]
5de4102dda fix(sales invoice): toggle Get Items From button based on is_return and POS view (backport #52594) (#54138)
Co-authored-by: NaviN <118178330+Navin-S-R@users.noreply.github.com>
Co-authored-by: Navin-S-R <navin@aerele.in>
fix(sales invoice): toggle Get Items From button based on is_return and POS view (#52594)
2026-04-09 11:58:07 +05:30
AarDG10
39a473455d refactor: update reset password method name
(cherry picked from commit c4d74483e1)
2026-04-09 06:23:37 +00:00
Frappe PR Bot
b88f3f69b0 chore(release): Bumped to Version 15.104.1
## [15.104.1](https://github.com/frappe/erpnext/compare/v15.104.0...v15.104.1) (2026-04-09)

### Bug Fixes

* last SLE not updated in the file ([3a2dc6f](3a2dc6f9ee))
2026-04-09 05:20:24 +00:00
rohitwaghchaure
dba8abbabf Merge pull request #54154 from frappe/mergify/bp/version-15/pr-54150
fix: last SLE not updated in the file (backport #54132) (backport #54150)
2026-04-09 10:48:53 +05:30
rohitwaghchaure
c1591c37db chore: fix conflicts
(cherry picked from commit c70259687a)
2026-04-09 04:48:38 +00:00
Rohit Waghchaure
3a2dc6f9ee fix: last SLE not updated in the file
(cherry picked from commit 38ed425ee2)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/test_work_order.py
(cherry picked from commit 8408e81335)
2026-04-09 04:48:38 +00:00
rohitwaghchaure
4f1203dbd0 Merge pull request #54150 from frappe/mergify/bp/version-15-hotfix/pr-54132
fix: last SLE not updated in the file (backport #54132)
2026-04-09 10:17:56 +05:30
rohitwaghchaure
c70259687a chore: fix conflicts 2026-04-09 09:06:31 +05:30
Rohit Waghchaure
8408e81335 fix: last SLE not updated in the file
(cherry picked from commit 38ed425ee2)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/test_work_order.py
2026-04-09 02:53:01 +00:00
mergify[bot]
a56d6984d1 fix: inventory dimension patch (backport #54147) (#54148) 2026-04-09 02:40:40 +00:00
mergify[bot]
deb67db4a0 fix: inventory dimension patch (backport #54141) (#54145)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: inventory dimension patch (#54141)
2026-04-09 02:01:45 +00:00
mergify[bot]
a26c845332 fix: inventory dimensions should not be mandatory unnecesarily (backport #54064) (#54133)
* fix: inventory dimensions should not be mandatory unnecesarily (#54064)

(cherry picked from commit 6e44b8913e)

# Conflicts:
#	erpnext/patches.txt
#	erpnext/stock/doctype/inventory_dimension/inventory_dimension.py

* chore: resolve conflicts

* chore: resolve conflicts

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-08 14:23:20 +00:00
mergify[bot]
bcd6d99549 fix: quality inspection item code fetch perm issue (backport #54121) (#54126)
Co-authored-by: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: quality inspection item code fetch perm issue (#54121)
2026-04-08 12:05:39 +00:00
rohitwaghchaure
d39d076fba Merge pull request #54118 from frappe/mergify/bp/version-15-hotfix/pr-54102
fix: hardcoded precision causing decimal issues (backport #54102)
2026-04-08 14:25:07 +05:30
rohitwaghchaure
21607f39c5 chore: fix conflicts 2026-04-08 14:05:04 +05:30
rohitwaghchaure
39a4760e07 chore: fix conflicts 2026-04-08 12:29:00 +05:30
rohitwaghchaure
d2c6a8958d chore: fix conflicts
Updated the modified date for the delivery note item.
2026-04-08 12:28:29 +05:30
Rohit Waghchaure
77545042a5 fix: hardcoded precision causing decimal issues
(cherry picked from commit 90fd6f2e40)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
#	erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
#	erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
2026-04-08 06:49:57 +00:00
Khushi Rawat
13f4ba1857 Merge pull request #54116 from frappe/mergify/bp/version-15-hotfix/pr-54103
fix: preserve asset movement field properties after save (backport #54103)
2026-04-08 12:15:45 +05:30
ravibharathi656
a87015e8e6 fix: preserve asset movement field properties after save
(cherry picked from commit 4a004a2a82)
2026-04-08 06:28:09 +00:00
Frappe PR Bot
a2626ed55f chore(release): Bumped to Version 15.104.0
# [15.104.0](https://github.com/frappe/erpnext/compare/v15.103.1...v15.104.0) (2026-04-07)

### Bug Fixes

* add support to fetch items based on manufacture stock entry; fix how it's done from work order ([e9ce0a4](e9ce0a41e6))
* add v15 compatibility for scrap item ([652bd39](652bd396d4))
* auto-set source_stock_entry ([b87b445](b87b445802))
* avg stock entries for disassembly from WO ([44d4079](44d40795df))
* correct warehouse preference for disassemble ([b8ddc2f](b8ddc2f2b9))
* create source_stock_entry to refer to original manufacturing entry ([55ee1dc](55ee1dcd04))
* custom button to disassemble manufactured stock entry with work order ([835ae27](835ae27b38))
* disassembly prompt with source stock entry field ([44f2e94](44f2e9480d))
* do not repost GL if no change in valuation ([0063201](0063201818))
* do not show inv dimension unnecessarily in stock entry (backport [#53946](https://github.com/frappe/erpnext/issues/53946)) ([#53950](https://github.com/frappe/erpnext/issues/53950)) ([e159c79](e159c79766))
* ensure compatibility with v15 ([8b42fcf](8b42fcf274))
* GL entries for different exchange rate in the purchase invoice ([def62cf](def62cf3fe))
* handle disassembly for secondary / scrap items ([229dc23](229dc23f97))
* include rejected qty in tax (purchase receipt) (backport [#53624](https://github.com/frappe/erpnext/issues/53624)) ([#53971](https://github.com/frappe/erpnext/issues/53971)) ([3fbfad1](3fbfad1b9b))
* manufacture entry with group_by support ([841b507](841b507502))
* **manufacturing:** handle null cur_dialog in BOM work order dialog (backport [#54011](https://github.com/frappe/erpnext/issues/54011)) ([#54014](https://github.com/frappe/erpnext/issues/54014)) ([cb0a548](cb0a548a95))
* not able to set operation in work order ([62d5870](62d58702a0))
* prevent selection of group type customer group in customer master ([7a227e0](7a227e048e))
* process loss with bom path disassembly ([eee6d7e](eee6d7e566))
* **promotional_scheme:** toggle enable state between Buying and Selli… (backport [#54110](https://github.com/frappe/erpnext/issues/54110)) ([#54111](https://github.com/frappe/erpnext/issues/54111)) ([5b7e6eb](5b7e6eb831))
* remove reference in serial/batch when document is cancelled (backport [#53979](https://github.com/frappe/erpnext/issues/53979)) ([#53988](https://github.com/frappe/erpnext/issues/53988)) ([e33abee](e33abeef7f))
* remove unnecessary param, and use value from self ([0b0dccd](0b0dccd294))
* resolve user permission error on status change by updating user … (backport [#54033](https://github.com/frappe/erpnext/issues/54033)) ([#54059](https://github.com/frappe/erpnext/issues/54059)) ([14085de](14085de332))
* set bom details on disassembly; abs batch qty ([84d5b52](84d5b52483))
* set serial and batch from source stock entry - on disassemble ([df049cd](df049cd277))
* set_query for source stock entry ([849b2e6](849b2e6ebf))
* show current stock qty in Stock Entry PDF (backport [#53761](https://github.com/frappe/erpnext/issues/53761)) ([#54031](https://github.com/frappe/erpnext/issues/54031)) ([af0116c](af0116cdc5))
* skip discount amount validation when not saving ([13eab9f](13eab9f993))
* **stock:** update stock queue in SABE for return entries ([05d6cf5](05d6cf5c9a))
* support creating disassembly (without link of WO) ([ef15c05](ef15c0581d))
* sync paid and received amount (backport [#53039](https://github.com/frappe/erpnext/issues/53039)) ([#54107](https://github.com/frappe/erpnext/issues/54107)) ([0505684](0505684d22))
* **test:** do not use is_group enabled customer group in test ([97684d3](97684d3dae))
* **test:** pin posting date in test_depreciation_on_cancel_invoice ([7f72189](7f72189665))
* **test:** use non-group customer group in test setup ([ea3fcc2](ea3fcc214b))
* transactions where update stock is 0 should not create SLEs (backport [#54035](https://github.com/frappe/erpnext/issues/54035)) ([#54076](https://github.com/frappe/erpnext/issues/54076)) ([bcf59e7](bcf59e7171))
* update min date based on transaction_date (backport [#53803](https://github.com/frappe/erpnext/issues/53803)) ([#54024](https://github.com/frappe/erpnext/issues/54024)) ([a71d32e](a71d32e668))
* use get_value ([8f01d12](8f01d12b5e))
* **ux:** refresh grid to correctly persist the state of fields ([3c327d5](3c327d5225))
* validate qty that can be disassembled from source stock entry. ([583c7b9](583c7b9819))
* validate work order consistency in stock entry ([d690a0c](d690a0c6bd))
* validation test for customer group ([7794f30](7794f3033e))
* **warehouse_capacity_dashboard:** removed `escape` from template (backport [#53907](https://github.com/frappe/erpnext/issues/53907)) ([#53908](https://github.com/frappe/erpnext/issues/53908)) ([efdb004](efdb004f0b))

### Features

* Allow Editing of Items and Quantities in Work Order ([1d36cb5](1d36cb55cd))
* croatian_address_template (backport [#53888](https://github.com/frappe/erpnext/issues/53888)) ([#54057](https://github.com/frappe/erpnext/issues/54057)) ([ee81268](ee812687e6))
* **timesheet:** allow partial billing and handled return ([21805bd](21805bde1f))

### Reverts

* botched backport ([#53967](https://github.com/frappe/erpnext/issues/53967)) ([22774fd](22774fdf87)), closes [#53776](https://github.com/frappe/erpnext/issues/53776) [#53766](https://github.com/frappe/erpnext/issues/53766) [#53767](https://github.com/frappe/erpnext/issues/53767)
2026-04-07 18:01:31 +00:00
diptanilsaha
0cc77274cb Merge pull request #54101 from frappe/version-15-hotfix 2026-04-07 22:19:03 +05:30
mergify[bot]
5b7e6eb831 fix(promotional_scheme): toggle enable state between Buying and Selli… (backport #54110) (#54111)
Co-authored-by: Ahmed AbuKhatwa <82771130+AhmedAbokhatwa@users.noreply.github.com>
Co-authored-by: AhmedAbukhatwa <Ahmedabukhatwa1@gmail.com>
fix(promotional_scheme): toggle enable state between Buying and Selli… (#54110)
2026-04-07 21:55:06 +05:30
rohitwaghchaure
1fb9c5244c Merge pull request #54009 from frappe/mergify/bp/version-15-hotfix/pr-53994
fix(stock): update stock queue in SABE for return entries (backport #53994)
2026-04-07 19:29:19 +05:30
rohitwaghchaure
e68eece3da Merge pull request #53804 from frappe/mergify/bp/version-15-hotfix/pr-52152
Refactor reposting feature (backport #52152)
2026-04-07 19:28:35 +05:30
Smit Vora
b8063a07fc Merge pull request #54097 from frappe/mergify/bp/version-15-hotfix/pr-53964
fix: consistently disassemble based on source  > SE / WO / BOM (backport #53964)
2026-04-07 19:27:23 +05:30
Smit Vora
8b42fcf274 fix: ensure compatibility with v15 2026-04-07 19:09:51 +05:30
Rohit Waghchaure
0063201818 fix: do not repost GL if no change in valuation 2026-04-07 18:52:10 +05:30
Rohit Waghchaure
2f9643d44d refactor: reposting for better peformance
(cherry picked from commit 20787ef5da)
2026-04-07 18:51:17 +05:30
mergify[bot]
0505684d22 fix: sync paid and received amount (backport #53039) (#54107)
Co-authored-by: Vishnu Priya Baskaran <145791817+ervishnucs@users.noreply.github.com>
fix: sync paid and received amount (#53039)
2026-04-07 13:06:02 +00:00
Nishka Gosalia
0b958136be Merge pull request #53996 from aerele/partial-billing-timesheet 2026-04-07 16:11:18 +05:30
Smit Vora
652bd396d4 fix: add v15 compatibility for scrap item 2026-04-07 15:43:43 +05:30
Smit Vora
904ac62830 chore: resolve conflicts 2026-04-07 14:51:35 +05:30
Smit Vora
2fee39017c Merge pull request #53974 from vorasmit/backport-50407-50856 2026-04-07 14:17:55 +05:30
Smit Vora
0b0dccd294 fix: remove unnecessary param, and use value from self
(cherry picked from commit 98dfd64f63)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py
2026-04-07 08:47:49 +00:00
Smit Vora
99df61a0d8 test: enhance tests as per review comments
(cherry picked from commit f13d37fbf9)
2026-04-07 08:47:48 +00:00
Smit Vora
7767659b87 test: maintain sufficient stock for scrap item
(cherry picked from commit b892139342)
2026-04-07 08:47:48 +00:00
Smit Vora
84d5b52483 fix: set bom details on disassembly; abs batch qty
(cherry picked from commit ab1fc22431)
2026-04-07 08:47:48 +00:00
Smit Vora
eee6d7e566 fix: process loss with bom path disassembly
(cherry picked from commit 93ad48bc1b)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/test_work_order.py
2026-04-07 08:47:47 +00:00
Smit Vora
d690a0c6bd fix: validate work order consistency in stock entry
(cherry picked from commit ea392b2009)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py
2026-04-07 08:47:47 +00:00
vorasmit
8f01d12b5e fix: use get_value
(cherry picked from commit a71e8bb116)
2026-04-07 08:47:46 +00:00
vorasmit
44d40795df fix: avg stock entries for disassembly from WO
(cherry picked from commit 71fd18bdf9)
2026-04-07 08:47:46 +00:00
vorasmit
841b507502 fix: manufacture entry with group_by support
(cherry picked from commit 3cf1ce8360)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py
2026-04-07 08:47:46 +00:00
Smit Vora
20f81516cf test: disassembly for scrap / secondary item
(cherry picked from commit a6d41151ff)
2026-04-07 08:47:45 +00:00
Smit Vora
229dc23f97 fix: handle disassembly for secondary / scrap items
(cherry picked from commit 2be8313819)
2026-04-07 08:47:45 +00:00
Smit Vora
f9b1df3572 test: disassembly of items with batch and serial numbers
(cherry picked from commit 1693698fed)
2026-04-07 08:47:44 +00:00
Smit Vora
1063a56251 test: additional items in stock entry considered with disassembly
(cherry picked from commit d32977e3a9)
2026-04-07 08:47:44 +00:00
Smit Vora
75eb5ad584 test: disassemble with source stock entry reference
(cherry picked from commit 6988e2cbbc)
2026-04-07 08:47:44 +00:00
Smit Vora
43c507570b test: disassembly from wo
(cherry picked from commit 342a14d340)
2026-04-07 08:47:43 +00:00
Smit Vora
df049cd277 fix: set serial and batch from source stock entry - on disassemble
(cherry picked from commit 13b019ab8e)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py
2026-04-07 08:47:43 +00:00
Smit Vora
b8ddc2f2b9 fix: correct warehouse preference for disassemble
(cherry picked from commit d3d6b5c660)
2026-04-07 08:47:43 +00:00
Smit Vora
b87b445802 fix: auto-set source_stock_entry
(cherry picked from commit 2e4e8bcaa7)
2026-04-07 08:47:42 +00:00
Smit Vora
e9ce0a41e6 fix: add support to fetch items based on manufacture stock entry; fix how it's done from work order
(cherry picked from commit 1ed0124ad7)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py
2026-04-07 08:47:42 +00:00
Smit Vora
583c7b9819 fix: validate qty that can be disassembled from source stock entry.
(cherry picked from commit 6394dead72)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/work_order.py
2026-04-07 08:47:41 +00:00
Smit Vora
ef15c0581d fix: support creating disassembly (without link of WO)
(cherry picked from commit dba82720b6)
2026-04-07 08:47:41 +00:00
Smit Vora
835ae27b38 fix: custom button to disassemble manufactured stock entry with work order
(cherry picked from commit b64f86148c)
2026-04-07 08:47:41 +00:00
Smit Vora
849b2e6ebf fix: set_query for source stock entry
(cherry picked from commit b47dfacb3e)
2026-04-07 08:47:40 +00:00
Smit Vora
44f2e9480d fix: disassembly prompt with source stock entry field
(cherry picked from commit 68e97808c5)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/work_order.js
#	erpnext/manufacturing/doctype/work_order/work_order.py
2026-04-07 08:47:40 +00:00
Smit Vora
55ee1dcd04 fix: create source_stock_entry to refer to original manufacturing entry
(cherry picked from commit d4baa9a74a)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.json
#	erpnext/stock/doctype/stock_entry/stock_entry.py
2026-04-07 08:47:40 +00:00
rohitwaghchaure
c81c1ea869 chore: fix test case 2026-04-07 13:11:44 +05:30
rohitwaghchaure
831ddcd5af Merge pull request #54068 from frappe/mergify/bp/version-15-hotfix/pr-54050
fix: GL entries for different exchange rate in the purchase invoice (backport #54050)
2026-04-07 13:10:40 +05:30
mergify[bot]
bcf59e7171 fix: transactions where update stock is 0 should not create SLEs (backport #54035) (#54076)
* fix: transactions where update stock is 0 should not create SLEs (#54035)

(cherry picked from commit 66780543bd)

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

* chore: resolve conflicts

* chore: resolve conflicts

---------

Co-authored-by: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-04-07 06:31:51 +00:00
mergify[bot]
ee812687e6 feat: croatian_address_template (backport #53888) (#54057)
Co-authored-by: mahsem <137205921+mahsem@users.noreply.github.com>
2026-04-07 10:26:03 +05:30
mergify[bot]
14085de332 fix: resolve user permission error on status change by updating user … (backport #54033) (#54059)
Co-authored-by: Krishna Shirsath <shirsathkrishna19@gmail.com>
2026-04-07 10:25:32 +05:30
Khushi Rawat
22652f30db Merge pull request #53956 from frappe/mergify/bp/version-15-hotfix/pr-53811
fix: prevent selection of group type customer group in customer master (backport #53811)
2026-04-07 03:16:36 +05:30
khushi8112
7794f3033e fix: validation test for customer group 2026-04-07 02:59:32 +05:30
Poovitha Palanivelu
21805bde1f feat(timesheet): allow partial billing and handled return 2026-04-06 23:04:23 +05:30
rohitwaghchaure
93bfd62725 chore: fix conflicts
Removed redundant calculation of billed quantity and adjusted logic for billed amount based on purchase order.
2026-04-06 17:44:36 +05:30
Rohit Waghchaure
def62cf3fe fix: GL entries for different exchange rate in the purchase invoice
(cherry picked from commit a953709640)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
2026-04-06 11:52:49 +00:00
khushi8112
ea3fcc214b fix(test): use non-group customer group in test setup 2026-04-06 16:09:05 +05:30
diptanilsaha
1146c9550a Merge pull request #54046 from frappe/mergify/bp/version-15-hotfix/pr-54042
fix: skip discount amount validation when not saving (backport #54042)
2026-04-06 13:44:03 +05:30
rohitwaghchaure
c5edeae97e Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr-53994 2026-04-06 13:35:08 +05:30
rohitwaghchaure
f855cc89c9 chore: fix conflicts 2026-04-06 13:12:03 +05:30
Sagar Vora
1ffbc399e1 test: add test for discount amount on partial purchase receipt
Co-authored-by: ravibharathi656 <131471282+ravibharathi656@users.noreply.github.com>
(cherry picked from commit 135cb5fd67)
2026-04-06 07:30:43 +00:00
Sagar Vora
13eab9f993 fix: skip discount amount validation when not saving
(cherry picked from commit 0975583388)
2026-04-06 07:30:43 +00:00
khushi8112
97684d3dae fix(test): do not use is_group enabled customer group in test
(cherry picked from commit 75fa2b2277)
2026-04-06 12:57:49 +05:30
khushi8112
7a227e048e fix: prevent selection of group type customer group in customer master
(cherry picked from commit 6068dc959f)
2026-04-06 12:57:49 +05:30
mergify[bot]
af0116cdc5 fix: show current stock qty in Stock Entry PDF (backport #53761) (#54031) 2026-04-06 05:36:09 +00:00
mergify[bot]
a71d32e668 fix: update min date based on transaction_date (backport #53803) (#54024)
Co-authored-by: Vishnu Priya Baskaran <145791817+ervishnucs@users.noreply.github.com>
fix: update min date based on transaction_date (#53803)
2026-04-05 21:16:48 +05:30
mergify[bot]
e33abeef7f fix: remove reference in serial/batch when document is cancelled (backport #53979) (#53988) 2026-04-05 15:39:20 +00:00
mergify[bot]
cb0a548a95 fix(manufacturing): handle null cur_dialog in BOM work order dialog (backport #54011) (#54014) 2026-04-05 12:48:16 +05:30
kavin-114
b57db06100 test(stock): add unit test to update stock queue for return
(cherry picked from commit e537896df8)

# Conflicts:
#	erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
2026-04-04 21:17:08 +00:00
kavin-114
05d6cf5c9a fix(stock): update stock queue in SABE for return entries
(cherry picked from commit 0af8077bcc)
2026-04-04 21:17:08 +00:00
Khushi Rawat
67183ad90c Merge pull request #53995 from khushi8112/fix-depreciation-cancel-test
fix(test): pin posting date in test_depreciation_on_cancel_invoice
2026-04-03 00:58:03 +05:30
khushi8112
7f72189665 fix(test): pin posting date in test_depreciation_on_cancel_invoice 2026-04-03 00:33:17 +05:30
Smit Vora
3c327d5225 fix(ux): refresh grid to correctly persist the state of fields 2026-04-01 08:58:00 +05:30
Rohit Waghchaure
62d58702a0 fix: not able to set operation in work order 2026-04-01 08:57:40 +05:30
Rohit Waghchaure
1d36cb55cd feat: Allow Editing of Items and Quantities in Work Order 2026-04-01 08:55:09 +05:30
mergify[bot]
3fbfad1b9b fix: include rejected qty in tax (purchase receipt) (backport #53624) (#53971)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: include rejected qty in tax (purchase receipt) (#53624)
2026-03-31 16:15:42 +00:00
Frappe PR Bot
2597eaad51 chore(release): Bumped to Version 15.103.1
## [15.103.1](https://github.com/frappe/erpnext/compare/v15.103.0...v15.103.1) (2026-03-31)

### Bug Fixes

* trigger release ([39aaefc](39aaefc202))

### Reverts

* botched backport (backport [#53967](https://github.com/frappe/erpnext/issues/53967)) ([#53968](https://github.com/frappe/erpnext/issues/53968)) ([75344e9](75344e9e82)), closes [#53776](https://github.com/frappe/erpnext/issues/53776) [#53766](https://github.com/frappe/erpnext/issues/53766) [#53767](https://github.com/frappe/erpnext/issues/53767)
2026-03-31 14:30:20 +00:00
Mihir Kandoi
39aaefc202 fix: trigger release 2026-03-31 19:58:36 +05:30
mergify[bot]
75344e9e82 revert: botched backport (backport #53967) (#53968)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix(manufacturing): apply work order status filter in job card (#53776)"
fix(manufacturing): apply work order status filter in job card (backport #53766) (#53767)"
2026-03-31 19:24:30 +05:30
Mihir Kandoi
22774fdf87 revert: botched backport (#53967)
fix(manufacturing): apply work order status filter in job card (#53776)"
fix(manufacturing): apply work order status filter in job card (backport #53766) (#53767)"
2026-03-31 13:52:02 +00:00
mergify[bot]
e159c79766 fix: do not show inv dimension unnecessarily in stock entry (backport #53946) (#53950)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: do not show inv dimension unnecessarily in stock entry (#53946)
2026-03-31 16:29:24 +05:30
mergify[bot]
94fe32f189 chore: remove inter warehouse transfer settings (backport #53860) (#53940)
* chore: remove inter warehouse transfer settings (#53860)

(cherry picked from commit 0696bd2082)

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

* chore: resolve conflicts

---------

Co-authored-by: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-31 06:08:02 +00:00
Frappe PR Bot
d39072a689 chore(release): Bumped to Version 15.103.0
# [15.103.0](https://github.com/frappe/erpnext/compare/v15.102.0...v15.103.0) (2026-03-30)

### Bug Fixes

* **bank_account:** added validation to fetch bank account details using `get_bank_account_details` (backport [#53926](https://github.com/frappe/erpnext/issues/53926)) ([#53929](https://github.com/frappe/erpnext/issues/53929)) ([d16061f](d16061f1bc))
* change shipment parcel dimension fields from Int to Float (backport [#53867](https://github.com/frappe/erpnext/issues/53867)) ([#53872](https://github.com/frappe/erpnext/issues/53872)) ([a21b82b](a21b82b238))
* **contract_template:** restrict `create`, `write` and `delete` access only to `System Manager` (backport [#53787](https://github.com/frappe/erpnext/issues/53787)) ([#53788](https://github.com/frappe/erpnext/issues/53788)) ([d50c727](d50c727f89))
* correct item valuation when "Deduct" is used in Purchase Invoice and Receipt. ([2585287](25852879f6))
* **email_campaign:** prevent unsubscribing entire campaign when email group member unsubscribes ([6151a49](6151a496e7))
* flaky currency exchange test (backport [#53813](https://github.com/frappe/erpnext/issues/53813)) ([#53816](https://github.com/frappe/erpnext/issues/53816)) ([d9cd09b](d9cd09b24a))
* invalid dynamic link filter for address doctype (backport [#53849](https://github.com/frappe/erpnext/issues/53849)) ([#53851](https://github.com/frappe/erpnext/issues/53851)) ([f7536f6](f7536f645b))
* **item_dashboard:** escaping `warehouse`, `item_code`, `stock_uom` and `item_name` on `get_data` (backport [#53904](https://github.com/frappe/erpnext/issues/53904)) ([#53912](https://github.com/frappe/erpnext/issues/53912)) ([db70d2e](db70d2e4df))
* **manufacturing:** apply work order status filter in job card ([#53776](https://github.com/frappe/erpnext/issues/53776)) ([78635eb](78635ebe99))
* **manufacturing:** apply work order status filter in job card (backport [#53766](https://github.com/frappe/erpnext/issues/53766)) ([#53767](https://github.com/frappe/erpnext/issues/53767)) ([d6afb9b](d6afb9b10a))
* **manufacturing:** close work order status when stock reservation is… (backport [#53714](https://github.com/frappe/erpnext/issues/53714)) ([#53720](https://github.com/frappe/erpnext/issues/53720)) ([468ca2b](468ca2bde1))
* **manufacturing:** update condition for base hour rate calculation ([#53777](https://github.com/frappe/erpnext/issues/53777)) ([64956ab](64956ab59c))
* **manufacturing:** update the qty precision (backport [#53874](https://github.com/frappe/erpnext/issues/53874)) ([#53884](https://github.com/frappe/erpnext/issues/53884)) ([46f751e](46f751e403))
* **opening_invoice_creation_tool:** sanitize summary content for dashboard (backport [#53917](https://github.com/frappe/erpnext/issues/53917)) ([#53923](https://github.com/frappe/erpnext/issues/53923)) ([b35a6c2](b35a6c2e73))
* purchase invoice for internal transfers should not require PO (backport [#53791](https://github.com/frappe/erpnext/issues/53791)) ([#53792](https://github.com/frappe/erpnext/issues/53792)) ([0a28fb3](0a28fb3ae1))
* purchase invoice missing item ([bcd56ab](bcd56abb62))
* **stock:** add warehouse filter to pick work order raw materials (backport [#53748](https://github.com/frappe/erpnext/issues/53748)) ([#53897](https://github.com/frappe/erpnext/issues/53897)) ([fffd3a7](fffd3a785c))
* **stock:** handle legacy single sle recon entries ([d09207a](d09207ab82))
* **stock:** update company validation for expense account in lcv ([40c2b3c](40c2b3c0f6))
* **templates:** escape attachment `file_url` and `file_name` in `order.html` and `projects.html` ([7b9f262](7b9f2626f8))
* **templates:** using correct syntax of `include` in `projects.html` ([979c594](979c594e98))
* **test:** enable perpetual inventory ([88c16c8](88c16c8378))
* validate if quantity greater than 0 in item dashboard (backport [#53846](https://github.com/frappe/erpnext/issues/53846)) ([#53847](https://github.com/frappe/erpnext/issues/53847)) ([ddf6eab](ddf6eab013))
* **warehouse_capacity_dashboard:** escaping `warehouse`, `item_code` and `company` on `get_data` (backport [#53894](https://github.com/frappe/erpnext/issues/53894)) ([#53899](https://github.com/frappe/erpnext/issues/53899)) ([1eda22c](1eda22c2bd))

### Features

* **report:** add service start/end date and amount with roll-ups in deferred revenue/expense report ([14088ee](14088ee7ac))
2026-03-30 18:03:28 +00:00
mergify[bot]
efdb004f0b fix(warehouse_capacity_dashboard): removed escape from template (backport #53907) (#53908)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(warehouse_capacity_dashboard): removed `escape` from template (#53907)
2026-03-30 23:33:06 +05:30
diptanilsaha
f4a1f04566 Merge pull request #53916 from frappe/version-15-hotfix 2026-03-30 23:31:54 +05:30
Lakshit Jain
01b8ae3e11 Merge pull request #53931 from frappe/mergify/bp/version-15-hotfix/pr-53406
fix: correct item valuation when "Deduct" is used in Purchase Invoice and Receipt. (backport #53406)
2026-03-30 20:39:14 +05:30
ljain112
b80e10e15e chore: resolve conflicts 2026-03-30 20:22:30 +05:30
ljain112
25852879f6 fix: correct item valuation when "Deduct" is used in Purchase Invoice and Receipt.
(cherry picked from commit e68f149d3a)

# Conflicts:
#	erpnext/controllers/buying_controller.py
2026-03-30 14:24:21 +00:00
mergify[bot]
d16061f1bc fix(bank_account): added validation to fetch bank account details using get_bank_account_details (backport #53926) (#53929)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(bank_account): added validation to fetch bank account details using `get_bank_account_details` (#53926)
2026-03-30 13:42:19 +00:00
mergify[bot]
b35a6c2e73 fix(opening_invoice_creation_tool): sanitize summary content for dashboard (backport #53917) (#53923)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(opening_invoice_creation_tool): sanitize summary content for dashboard (#53917)
2026-03-30 13:40:32 +00:00
rohitwaghchaure
5884b71b0a Merge pull request #53910 from frappe/mergify/bp/version-15-hotfix/pr-53906
fix: purchase invoice missing item (backport #53906)
2026-03-30 18:39:08 +05:30
Rohit Waghchaure
bcd56abb62 fix: purchase invoice missing item
(cherry picked from commit af994c1a22)
2026-03-30 18:24:24 +05:30
mergify[bot]
db70d2e4df fix(item_dashboard): escaping warehouse, item_code, stock_uom and item_name on get_data (backport #53904) (#53912)
* fix(item_dashboard): escaping `warehouse`, `item_code`, `stock_uom` and `item_name` on `get_data` (#53904)

(cherry picked from commit fa5238ba12)

# Conflicts:
#	erpnext/stock/dashboard/item_dashboard.py

* chore: resolve conflict

---------

Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-03-30 09:55:57 +00:00
rohitwaghchaure
098cbcde10 Merge pull request #53895 from frappe/mergify/bp/version-15-hotfix/pr-53799
fix(stock): update company validation for expense account in lcv (backport #53799)
2026-03-30 14:45:20 +05:30
mergify[bot]
1eda22c2bd fix(warehouse_capacity_dashboard): escaping warehouse, item_code and company on get_data (backport #53894) (#53899)
* fix(warehouse_capacity_dashboard): escaping `warehouse`, `item_code` and `company` on `get_data` (#53894)

(cherry picked from commit ddeb9775ed)

# Conflicts:
#	erpnext/stock/dashboard/warehouse_capacity_dashboard.py

* chore: resolve conflicts

---------

Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-03-30 08:34:37 +00:00
mergify[bot]
fffd3a785c fix(stock): add warehouse filter to pick work order raw materials (backport #53748) (#53897)
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
fix(stock): add warehouse filter to pick work order raw materials (#53748)
2026-03-30 08:02:27 +00:00
Sudharsanan11
88c16c8378 fix(test): enable perpetual inventory
(cherry picked from commit 875a2e4947)
2026-03-30 07:34:27 +00:00
Sudharsanan11
40c2b3c0f6 fix(stock): update company validation for expense account in lcv
(cherry picked from commit 913168e8b6)
2026-03-30 07:34:27 +00:00
mergify[bot]
46f751e403 fix(manufacturing): update the qty precision (backport #53874) (#53884)
* fix(manufacturing): update the qty precision (#53874)

(cherry picked from commit f3a794384a)

# Conflicts:
#	erpnext/manufacturing/doctype/production_plan/production_plan.py

* chore: resolve conflicts

---------

Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-29 16:38:51 +00:00
mergify[bot]
a21b82b238 fix: change shipment parcel dimension fields from Int to Float (backport #53867) (#53872)
* fix: change shipment parcel dimension fields from Int to Float (#53867)

(cherry picked from commit 6badf00313)

# Conflicts:
#	erpnext/stock/doctype/shipment_parcel/shipment_parcel.json
#	erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.json

* chore: resolve conflicts

* chore: resole conflicts

---------

Co-authored-by: Kaushal Shriwas <64089478+kaulith@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-29 07:06:55 +00:00
mergify[bot]
f7536f645b fix: invalid dynamic link filter for address doctype (backport #53849) (#53851) 2026-03-27 12:38:29 +00:00
mergify[bot]
ddf6eab013 fix: validate if quantity greater than 0 in item dashboard (backport #53846) (#53847)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: validate if quantity greater than 0 in item dashboard (#53846)
2026-03-27 16:03:48 +05:30
ruthra kumar
3e4c331962 Merge pull request #53829 from frappe/mergify/bp/version-15-hotfix/pr-53429
feat(report): add service start/end date and amount with roll-ups in deferred revenue/expense report (backport #53429)
2026-03-27 10:53:35 +05:30
ruthra kumar
4b1c1d33b0 Merge pull request #53827 from frappe/mergify/bp/version-15-hotfix/pr-53343
fix(email_campaign): prevent unsubscribing entire campaign when email group member unsubscribes (backport #53343)
2026-03-27 10:43:29 +05:30
Shllokkk
14088ee7ac feat(report): add service start/end date and amount with roll-ups in deferred revenue/expense report
(cherry picked from commit 8e5692d8a3)
2026-03-27 05:00:35 +00:00
Shllokkk
6151a496e7 fix(email_campaign): prevent unsubscribing entire campaign when email group member unsubscribes
(cherry picked from commit 56f597f5ad)
2026-03-27 04:57:30 +00:00
rohitwaghchaure
237915dc03 Merge pull request #53809 from frappe/mergify/bp/version-15-hotfix/pr-53216
fix(stock): handle legacy single sle recon entries (backport #53216)
2026-03-26 18:27:19 +05:30
kavin-114
d09207ab82 fix(stock): handle legacy single sle recon entries
(cherry picked from commit 7e6bbcc3fb)
2026-03-26 18:12:09 +05:30
mergify[bot]
d9cd09b24a fix: flaky currency exchange test (backport #53813) (#53816) 2026-03-26 12:38:36 +00:00
mergify[bot]
0a28fb3ae1 fix: purchase invoice for internal transfers should not require PO (backport #53791) (#53792)
* fix: purchase invoice for internal transfers should not require PO (#53791)

(cherry picked from commit 3f74733942)

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

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-26 03:45:18 +00:00
mergify[bot]
d50c727f89 fix(contract_template): restrict create, write and delete access only to System Manager (backport #53787) (#53788)
* fix(contract_template): restrict `create`, `write` and `delete` access only to `System Manager` (#53787)

(cherry picked from commit e136bfbb61)

# Conflicts:
#	erpnext/crm/doctype/contract_template/contract_template.json

* chore: resolve conflicts

---------

Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-03-25 15:05:12 +00:00
diptanilsaha
40c8201302 Merge pull request #53780 from frappe/mergify/bp/version-15-hotfix/pr-53779
fix(template): escape attachment `file_url` and `file_name` and jinja syntax (backport #53779)
2026-03-25 15:20:01 +05:30
diptanilsaha
979c594e98 fix(templates): using correct syntax of include in projects.html
(cherry picked from commit bc6561cdd0)
2026-03-25 09:28:18 +00:00
diptanilsaha
7b9f2626f8 fix(templates): escape attachment file_url and file_name in order.html and projects.html
(cherry picked from commit d9760bbf4f)
2026-03-25 09:28:18 +00:00
Pandiyan P
64956ab59c fix(manufacturing): update condition for base hour rate calculation (#53777) 2026-03-25 13:49:31 +05:30
Pandiyan P
78635ebe99 fix(manufacturing): apply work order status filter in job card (#53776) 2026-03-25 13:24:07 +05:30
mergify[bot]
d6afb9b10a fix(manufacturing): apply work order status filter in job card (backport #53766) (#53767)
Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
fix(manufacturing): apply work order status filter in job card (#53766)
2026-03-25 11:21:31 +05:30
mergify[bot]
468ca2bde1 fix(manufacturing): close work order status when stock reservation is… (backport #53714) (#53720)
Co-authored-by: Pandiyan P <pandiyanpalani37@gmail.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix(manufacturing): close work order status when stock reservation is… (#53714)
2026-03-23 16:28:01 +00:00
Frappe PR Bot
1d14ba1639 chore(release): Bumped to Version 15.102.0
# [15.102.0](https://github.com/frappe/erpnext/compare/v15.101.3...v15.102.0) (2026-03-23)

### Bug Fixes

* Adding validation for operation time in BOM ([7707a79](7707a79d44))
* batch validation for subcontracting receipt ([32c0532](32c0532dec))
* **budget-variance-report:** validate 'budget_against' filter (backport [#53079](https://github.com/frappe/erpnext/issues/53079)) ([#53663](https://github.com/frappe/erpnext/issues/53663)) ([d96590c](d96590c4d9))
* check for `submit` permissions instead of `write` permissions when updating status (backport [#53697](https://github.com/frappe/erpnext/issues/53697)) ([#53702](https://github.com/frappe/erpnext/issues/53702)) ([46e784d](46e784d094))
* check posting_date in args (backport [#53303](https://github.com/frappe/erpnext/issues/53303)) ([#53611](https://github.com/frappe/erpnext/issues/53611)) ([e0f1e75](e0f1e757f3))
* consider returned qty in subcontracting report (backport [#53616](https://github.com/frappe/erpnext/issues/53616)) ([#53620](https://github.com/frappe/erpnext/issues/53620)) ([af86fd3](af86fd3cb4))
* deadlock issue for SLE ([540a854](540a8540d6))
* do not overwrite expense account in stock entry (backport [#53658](https://github.com/frappe/erpnext/issues/53658)) ([#53660](https://github.com/frappe/erpnext/issues/53660)) ([90e4f90](90e4f9026d))
* ignore cost center (backport [#53063](https://github.com/frappe/erpnext/issues/53063)) ([#53613](https://github.com/frappe/erpnext/issues/53613)) ([562f93e](562f93e75c))
* incorrect sle calculation when doc has project ([#53599](https://github.com/frappe/erpnext/issues/53599)) ([7acd435](7acd435835))
* initialize all tax columns to resolve Key error in `item_wise_sales_register` and `item_wise_purchase_register` reports (backport [#53323](https://github.com/frappe/erpnext/issues/53323)) ([#53583](https://github.com/frappe/erpnext/issues/53583)) ([119195c](119195c6fa))
* **manufacturing:** update non-stock item dict (backport [#53689](https://github.com/frappe/erpnext/issues/53689)) ([#53698](https://github.com/frappe/erpnext/issues/53698)) ([c0ce34e](c0ce34e12c))
* merge conflicts ([b3f0e2a](b3f0e2a00d))
* PO should not be required for internal transfers (backport [#53681](https://github.com/frappe/erpnext/issues/53681)) ([#53683](https://github.com/frappe/erpnext/issues/53683)) ([04d74ad](04d74ad6eb))
* python error in manufacture entry if transfer against is job card (backport [#53615](https://github.com/frappe/erpnext/issues/53615)) ([#53617](https://github.com/frappe/erpnext/issues/53617)) ([5a3bc27](5a3bc27e2c))
* **sales_invoice:** using `msgprint` and removed condition checking for `is_created_using_pos` to refetch payment methods ([#53636](https://github.com/frappe/erpnext/issues/53636)) ([f8ab56e](f8ab56ecc9))
* set customer details on customer creation at login ([#53509](https://github.com/frappe/erpnext/issues/53509)) ([4f39dfd](4f39dfd642))
* shipping rule applied twice on non stock items (backport [#53655](https://github.com/frappe/erpnext/issues/53655)) ([#53686](https://github.com/frappe/erpnext/issues/53686)) ([5e767ea](5e767ea595))
* stock queue for SABB ([461bc17](461bc1733f))
* **stock:** add company filter while fetching batches (backport [#53369](https://github.com/frappe/erpnext/issues/53369)) ([#53580](https://github.com/frappe/erpnext/issues/53580)) ([c09c599](c09c5999dc))
* **stock:** fix email error message (backport [#53606](https://github.com/frappe/erpnext/issues/53606)) ([#53632](https://github.com/frappe/erpnext/issues/53632)) ([6ea3d56](6ea3d56972))
* **trends:** added validation for `period_based_on` filter (backport [#53690](https://github.com/frappe/erpnext/issues/53690)) ([#53691](https://github.com/frappe/erpnext/issues/53691)) ([974755b](974755b224))
* validate permission before updating status (backport [#53651](https://github.com/frappe/erpnext/issues/53651)) ([#53652](https://github.com/frappe/erpnext/issues/53652)) ([defa1d4](defa1d4a76))

### Features

* add cost center field to the stock entry accounting dimension tab ([e17b5df](e17b5dfe61))
2026-03-23 14:59:14 +00:00
diptanilsaha
a270c02bb4 Merge pull request #53700 from frappe/version-15-hotfix 2026-03-23 20:27:38 +05:30
rohitwaghchaure
28aa21bf83 Merge pull request #53706 from frappe/mergify/bp/version-15-hotfix/pr-53705
fix: batch validation for subcontracting receipt (backport #53705)
2026-03-23 18:52:47 +05:30
mergify[bot]
119195c6fa fix: initialize all tax columns to resolve Key error in item_wise_sales_register and item_wise_purchase_register reports (backport #53323) (#53583)
Co-authored-by: Lakshit Jain <ljain112@gmail.com>
fix: initialize all tax columns to resolve Key error in `item_wise_sales_register` and `item_wise_purchase_register` reports (#53323)
2026-03-23 18:05:54 +05:30
Rohit Waghchaure
32c0532dec fix: batch validation for subcontracting receipt
(cherry picked from commit b8d201658a)
2026-03-23 11:32:42 +00:00
diptanilsaha
46e784d094 fix: check for submit permissions instead of write permissions when updating status (backport #53697) (#53702) 2026-03-23 16:03:50 +05:30
mergify[bot]
c0ce34e12c fix(manufacturing): update non-stock item dict (backport #53689) (#53698)
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix(manufacturing): update non-stock item dict (#53689)
2026-03-23 10:26:29 +00:00
Mihir Kandoi
eaf5494502 chore: linter (#53696) 2026-03-23 10:19:41 +00:00
mergify[bot]
04d74ad6eb fix: PO should not be required for internal transfers (backport #53681) (#53683)
* fix: PO should not be required for internal transfers (#53681)

(cherry picked from commit 5154102468)

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

* chore: resolve conflicts

* chore: resolve conflicts

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-23 09:30:20 +00:00
Nishka Gosalia
2de04b8a46 Merge pull request #53693 from frappe/mergify/bp/version-15-hotfix/pr-53649 2026-03-23 14:46:49 +05:30
mergify[bot]
974755b224 fix(trends): added validation for period_based_on filter (backport #53690) (#53691)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(trends): added validation for `period_based_on` filter (#53690)
2026-03-23 14:42:03 +05:30
mergify[bot]
5e767ea595 fix: shipping rule applied twice on non stock items (backport #53655) (#53686)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: shipping rule applied twice on non stock items (#53655)
2026-03-23 14:38:32 +05:30
nishkagosalia
f5bd85b4dc chore: Adding new argument in status updater to skip qty validation
(cherry picked from commit dcd0509089)
2026-03-23 08:57:35 +00:00
rohitwaghchaure
3fcf6cfef7 Merge pull request #53674 from frappe/mergify/bp/version-15-hotfix/pr-53673
fix: stock queue for SABB (backport #53673)
2026-03-22 13:20:05 +05:30
Rohit Waghchaure
461bc1733f fix: stock queue for SABB
(cherry picked from commit 3fcf308ed8)
2026-03-22 07:30:44 +00:00
rohitwaghchaure
812ca37055 Merge pull request #53669 from frappe/mergify/bp/version-15-hotfix/pr-53638
fix: deadlock issue for SLE (backport #53638)
2026-03-21 14:41:01 +05:30
Rohit Waghchaure
540a8540d6 fix: deadlock issue for SLE
(cherry picked from commit f48b03c6ec)
2026-03-21 08:34:28 +00:00
diptanilsaha
d96590c4d9 fix(budget-variance-report): validate 'budget_against' filter (backport #53079) (#53663) 2026-03-20 15:33:36 +05:30
mergify[bot]
90e4f9026d fix: do not overwrite expense account in stock entry (backport #53658) (#53660)
* fix: do not overwrite expense account in stock entry (#53658)

(cherry picked from commit fa35fbdb8e)

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

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-20 09:41:39 +00:00
mergify[bot]
defa1d4a76 fix: validate permission before updating status (backport #53651) (#53652)
* fix: validate permission before updating status (#53651)

(cherry picked from commit 8e17c722fb)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.py
#	erpnext/selling/doctype/sales_order/sales_order.py
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.py

* chore: resolve conflicts

---------

Co-authored-by: diptanilsaha <diptanil@frappe.io>
2026-03-19 20:49:53 +05:30
Nishka Gosalia
ff11429941 Merge pull request #53647 from frappe/mergify/bp/version-15-hotfix/pr-53645
fix: Adding validation for operation time in BOM (backport #53645)
2026-03-19 20:21:20 +05:30
Nishka Gosalia
b3f0e2a00d fix: merge conflicts 2026-03-19 19:54:53 +05:30
nishkagosalia
7707a79d44 fix: Adding validation for operation time in BOM
(cherry picked from commit 7f70e62c30)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/test_job_card.py
2026-03-19 13:00:08 +00:00
Frappe PR Bot
94900cb8b8 chore(release): Bumped to Version 15.101.3
## [15.101.3](https://github.com/frappe/erpnext/compare/v15.101.2...v15.101.3) (2026-03-19)

### Bug Fixes

* **sales_invoice:** using `msgprint` and removed condition checking for `is_created_using_pos` to refetch payment methods ([#53636](https://github.com/frappe/erpnext/issues/53636)) ([65d8a17](65d8a176a6))
2026-03-19 10:07:46 +00:00
diptanilsaha
c1be262357 Merge pull request #53639 from frappe/mergify/bp/version-15/pr-53636
fix(sales_invoice): using `msgprint` and removed condition checking for `is_created_using_pos` to refetch payment methods (backport #53636)
2026-03-19 15:36:17 +05:30
diptanilsaha
65d8a176a6 fix(sales_invoice): using msgprint and removed condition checking for is_created_using_pos to refetch payment methods (#53636)
(cherry picked from commit f8ab56ecc9)
2026-03-19 08:49:49 +00:00
diptanilsaha
f8ab56ecc9 fix(sales_invoice): using msgprint and removed condition checking for is_created_using_pos to refetch payment methods (#53636) 2026-03-19 14:18:48 +05:30
Ravibharathi
488ea7f994 Merge pull request #53628 from frappe/mergify/bp/version-15-hotfix/pr-53509
fix: set customer details on customer creation at login (backport #53509)
2026-03-19 13:59:27 +05:30
mergify[bot]
6ea3d56972 fix(stock): fix email error message (backport #53606) (#53632)
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
fix(stock): fix email error message (#53606)
2026-03-19 07:39:30 +00:00
Navin-S-R
e2c8dc5386 chore: resolve conflict 2026-03-19 13:06:16 +05:30
mergify[bot]
9c243e8dd0 refactor: remove test file import in stock ageing report (backport #53619) (#53625)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-19 06:50:54 +00:00
Sakthivel Murugan S
4f39dfd642 fix: set customer details on customer creation at login (#53509)
(cherry picked from commit 256d267a3b)

# Conflicts:
#	erpnext/portal/utils.py
2026-03-19 06:34:50 +00:00
mergify[bot]
af86fd3cb4 fix: consider returned qty in subcontracting report (backport #53616) (#53620)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: consider returned qty in subcontracting report (#53616)
2026-03-19 11:52:44 +05:30
mergify[bot]
5a3bc27e2c fix: python error in manufacture entry if transfer against is job card (backport #53615) (#53617)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: python error in manufacture entry if transfer against is job card (#53615)
2026-03-19 05:13:36 +00:00
mergify[bot]
562f93e75c fix: ignore cost center (backport #53063) (#53613)
Co-authored-by: Sowmya <106989392+SowmyaArunachalam@users.noreply.github.com>
fix: ignore cost center (#53063)
2026-03-19 04:41:10 +00:00
mergify[bot]
e0f1e757f3 fix: check posting_date in args (backport #53303) (#53611)
Co-authored-by: Vishnu Priya Baskaran <145791817+ervishnucs@users.noreply.github.com>
fix: check posting_date in args (#53303)
2026-03-19 04:38:20 +00:00
Frappe PR Bot
572d8530b6 chore(release): Bumped to Version 15.101.2
## [15.101.2](https://github.com/frappe/erpnext/compare/v15.101.1...v15.101.2) (2026-03-18)

### Bug Fixes

* incorrect sle calculation when doc has project ([#53599](https://github.com/frappe/erpnext/issues/53599)) ([9e10dec](9e10dec903))
2026-03-18 13:42:20 +00:00
rohitwaghchaure
0fa8cc76f5 Merge pull request #53602 from frappe/mergify/bp/version-15/pr-53600
fix: incorrect sle calculation when doc has project (backport #53599) (backport #53600)
2026-03-18 19:10:44 +05:30
Mihir Kandoi
9e10dec903 fix: incorrect sle calculation when doc has project (#53599)
(cherry picked from commit 6cb6a52ded)
(cherry picked from commit 7acd435835)
2026-03-18 13:38:17 +00:00
rohitwaghchaure
7b64f88734 Merge pull request #53600 from frappe/mergify/bp/version-15-hotfix/pr-53599
fix: incorrect sle calculation when doc has project (backport #53599)
2026-03-18 19:07:26 +05:30
Mihir Kandoi
7acd435835 fix: incorrect sle calculation when doc has project (#53599)
(cherry picked from commit 6cb6a52ded)
2026-03-18 13:20:17 +00:00
rohitwaghchaure
16fe458b92 Merge pull request #53585 from frappe/mergify/bp/version-15-hotfix/pr-53246
feat: add cost center field to the stock entry accounting dimension tab (backport #53246)
2026-03-18 16:52:27 +05:30
rohitwaghchaure
4c2dba98da chore: fix conflicts
Removed several fields related to additional transfer entries and subcontracting inward orders from the stock entry JSON.
2026-03-18 16:32:56 +05:30
sudarshan-g
e17b5dfe61 feat: add cost center field to the stock entry accounting dimension tab
(cherry picked from commit 47772f4e77)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.json
2026-03-18 06:20:08 +00:00
mergify[bot]
c09c5999dc fix(stock): add company filter while fetching batches (backport #53369) (#53580)
* fix(stock): add company filter while fetching batches (#53369)

(cherry picked from commit 31d14df37b)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/work_order.py
#	erpnext/stock/doctype/pick_list/pick_list.py
#	erpnext/stock/doctype/pick_list_item/pick_list_item.json

* chore: resolve conflicts

* chore: resolve conflicts

* chore: resolve conflicts

* chore: resolve conflicts

---------

Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-18 05:04:52 +00:00
mergify[bot]
a7bf55b4bf chore: make supplier data expanded by default in PI (backport #53565) (#53578)
* chore: make supplier data expanded by default in PI (#53565)

(cherry picked from commit b433852f8a)

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

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-18 04:59:04 +00:00
Frappe PR Bot
c912df95cb chore(release): Bumped to Version 15.101.1
## [15.101.1](https://github.com/frappe/erpnext/compare/v15.101.0...v15.101.1) (2026-03-18)

### Bug Fixes

* add item_name to quick entry fields in Item doctype (backport [#53530](https://github.com/frappe/erpnext/issues/53530)) ([#53532](https://github.com/frappe/erpnext/issues/53532)) ([0e770c0](0e770c0bbd))
* Append existing ignored doctypes in Journal Entry on_cancel instead of overwriting ([b73d970](b73d9700d0))
* **banking:** include paid purchase invoices in reports and bank clearance ([#52675](https://github.com/frappe/erpnext/issues/52675)) ([ab9d960](ab9d960aa8))
* broke cost center filter in get outstanding reference docs ([53e3bfb](53e3bfbf22))
* change "Date" label to "Posting Date" in Sales Invoice and Purchase Invoice (backport [#53503](https://github.com/frappe/erpnext/issues/53503)) ([#53516](https://github.com/frappe/erpnext/issues/53516)) ([eec8cf8](eec8cf8a71))
* coderebbit review ([05d614e](05d614eb04))
* correct function syntax in TDS Computation Report ([94972da](94972da845))
* correct overlap detection in JobCard.has_overlap (backport [#53473](https://github.com/frappe/erpnext/issues/53473)) ([#53522](https://github.com/frappe/erpnext/issues/53522)) ([d262a65](d262a65b00))
* correct payment terms fetching and recalculation logic ([79b0482](79b04826d9))
* correct payment terms fetching and recalculation logic ([3148816](3148816451))
* Creating new item price incase of changes in expired item price (backport [#53534](https://github.com/frappe/erpnext/issues/53534)) ([#53544](https://github.com/frappe/erpnext/issues/53544)) ([526ffc1](526ffc1176))
* **delivery note:** avoid maintaining si_detail on return delivery note (backport [#52456](https://github.com/frappe/erpnext/issues/52456)) ([#53352](https://github.com/frappe/erpnext/issues/53352)) ([034d460](034d460ae1))
* do not modify rate in the child item merely for comparison (backport [#53301](https://github.com/frappe/erpnext/issues/53301)) ([#53375](https://github.com/frappe/erpnext/issues/53375)) ([0e00ab8](0e00ab8865))
* do not set valuation rate for invoice without update stock ([284ccd1](284ccd1def))
* enhance sorting and optimize GL entry retrieval ([93ebec9](93ebec90ef))
* **italy:** fix e-invoice ScontoMaggiorazione structure and included_in_print_rate support ([#53334](https://github.com/frappe/erpnext/issues/53334)) ([b9c8e8d](b9c8e8d478))
* **manufacturing:** update working hours validation (backport [#53559](https://github.com/frappe/erpnext/issues/53559)) ([#53566](https://github.com/frappe/erpnext/issues/53566)) ([9771ed4](9771ed4c57))
* **minor:** filter bank accounts in bank statement import ([#53481](https://github.com/frappe/erpnext/issues/53481)) ([a5d1afe](a5d1afe304))
* NoneType error when template description is to be copied to variant (backport [#53358](https://github.com/frappe/erpnext/issues/53358)) ([#53365](https://github.com/frappe/erpnext/issues/53365)) ([0612f1c](0612f1c941))
* **p&l_statement:** disable accumulated value filter by default (backport [#53488](https://github.com/frappe/erpnext/issues/53488)) ([#53489](https://github.com/frappe/erpnext/issues/53489)) ([b63b532](b63b5320f2))
* precision issue in production plan (backport [#53370](https://github.com/frappe/erpnext/issues/53370)) ([#53373](https://github.com/frappe/erpnext/issues/53373)) ([5737d2a](5737d2afa3))
* re-calculate taxes and totals after resetting bundle item rate (backport [#53342](https://github.com/frappe/erpnext/issues/53342)) ([#53349](https://github.com/frappe/erpnext/issues/53349)) ([db251c6](db251c6e11))
* refactor GL entry mapping to include voucher type ([c2e6759](c2e67599f5))
* **regional:** rename duplicate Customer fields in Italy setup (backport [#50921](https://github.com/frappe/erpnext/issues/50921)) ([#53397](https://github.com/frappe/erpnext/issues/53397)) ([2a70203](2a70203cab))
* remove redundant pos print format ([#53348](https://github.com/frappe/erpnext/issues/53348)) ([8497d1f](8497d1f8cf))
* sales order indicator should be based on available qty rather th… (backport [#53456](https://github.com/frappe/erpnext/issues/53456)) ([#53457](https://github.com/frappe/erpnext/issues/53457)) ([a6cf31e](a6cf31edad))
* **sales_invoice:** reset payment methods on `pos_profile` change (backport [#53514](https://github.com/frappe/erpnext/issues/53514)) ([#53560](https://github.com/frappe/erpnext/issues/53560)) ([239728e](239728e4d9))
* **serial_and_batch_bundle_selector:** handle CSV attachment properly (backport [#53460](https://github.com/frappe/erpnext/issues/53460)) ([#53461](https://github.com/frappe/erpnext/issues/53461)) ([7a7c4a0](7a7c4a03f0))
* skip validate_stock_accounts when perpetual inventory is disabled ([3bc9190](3bc9190795))
* stock adjustment entry ([ac6c06d](ac6c06daf9))
* **stock:** fix the property setter (backport [#53422](https://github.com/frappe/erpnext/issues/53422)) ([#53573](https://github.com/frappe/erpnext/issues/53573)) ([57815a0](57815a07ac))
* **support-settings:** disable the auto-close tickets feature if `close_issue_after_days` is set to 0 (backport [#53499](https://github.com/frappe/erpnext/issues/53499)) ([#53504](https://github.com/frappe/erpnext/issues/53504)) ([30fe711](30fe711c44))
* **tds-report:** correct party type filtering and refactor ([e5eb540](e5eb5406da))
* test case ([c384564](c384564314))
* update delivery date in line items ([#53331](https://github.com/frappe/erpnext/issues/53331)) ([85c4cc3](85c4cc3e1b))
* update item description in Production Plan Assembly Items table ([e3e9d7b](e3e9d7b19e))
* update label on company change ([908e185](908e185cfe))
* update user status depends on employee status ([c5796fe](c5796fed4a))
* use completion_date not posting date ([6d47660](6d476604df))
* use qb to prevent incorrect sql due to user permissions ([03f0922](03f09222cf))
* valuation rate for no Use Batch wise Valuation batches ([ca6872c](ca6872c768))
2026-03-18 04:58:29 +00:00
diptanilsaha
915315ef1b Merge pull request #53541 from frappe/version-15-hotfix 2026-03-18 10:26:58 +05:30
mergify[bot]
526dc68c72 chore: add documentation link in valuation method field (backport #53564) (#53570)
* chore: add documentation link in valuation method field (#53564)

(cherry picked from commit f319857939)

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

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-17 16:50:12 +00:00
mergify[bot]
9771ed4c57 fix(manufacturing): update working hours validation (backport #53559) (#53566)
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
fix(manufacturing): update working hours validation (#53559)
2026-03-17 22:09:00 +05:30
mergify[bot]
57815a07ac fix(stock): fix the property setter (backport #53422) (#53573)
Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
fix(stock): fix the property setter (#53422)
2026-03-17 22:07:46 +05:30
Arturo
b9c8e8d478 fix(italy): fix e-invoice ScontoMaggiorazione structure and included_in_print_rate support (#53334) 2026-03-17 15:18:14 +00:00
mergify[bot]
526ffc1176 fix: Creating new item price incase of changes in expired item price (backport #53534) (#53544)
Co-authored-by: Nishka Gosalia <nishkagosalia@Nishkas-MacBook-Air.local>
Co-authored-by: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com>
fix: Creating new item price incase of changes in expired item price (#53534)
2026-03-17 14:51:13 +00:00
mergify[bot]
239728e4d9 fix(sales_invoice): reset payment methods on pos_profile change (backport #53514) (#53560)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(sales_invoice): reset payment methods on `pos_profile` change (#53514)
2026-03-17 20:17:32 +05:30
rohitwaghchaure
48d211f8a0 Merge pull request #53554 from saeedkola/fix/validate-stock-accounts-perpetual-v15-hotfix
fix: skip validate_stock_accounts in Journal Entry when perpetual inventory is disabled
2026-03-17 18:30:34 +05:30
ruthra kumar
761caba8e8 Merge pull request #53549 from frappe/mergify/bp/version-15-hotfix/pr-53548
fix: incorrect user perms in queries (backport #53548)
2026-03-17 17:14:04 +05:30
Saeed Kola
3bc9190795 fix: skip validate_stock_accounts when perpetual inventory is disabled
When perpetual inventory is disabled, stock transactions produce no GL
entries, so blocking manual Journal Entries against stock accounts is
incorrect. Added an early return guard in validate_stock_accounts()
to skip the check when is_perpetual_inventory_enabled() returns False.
2026-03-17 17:08:29 +05:30
ruthra kumar
3720a8d5c9 Merge pull request #53528 from frappe/mergify/bp/version-15-hotfix/pr-52675
fix(banking): include paid purchase invoices in reports and bank clearance (backport #52675)
2026-03-17 17:01:33 +05:30
ruthra kumar
03f09222cf fix: use qb to prevent incorrect sql due to user permissions
(cherry picked from commit 04b967bd6d)

# Conflicts:
#	erpnext/controllers/queries.py
2026-03-17 16:45:29 +05:30
ruthra kumar
f232024fa4 chore: remove incorrect import
(cherry picked from commit fc2edfbded)

# Conflicts:
#	erpnext/controllers/queries.py
2026-03-17 11:02:32 +00:00
rohitwaghchaure
fd336e8d4b Merge pull request #53537 from frappe/mergify/bp/version-15-hotfix/pr-53500
fix: valuation rate for no Use Batch wise Valuation batches (backport #53500)
2026-03-17 15:53:29 +05:30
rohitwaghchaure
c384564314 fix: test case
Removed company parameter from get_valuation_method call.
2026-03-17 15:05:46 +05:30
Rohit Waghchaure
ca6872c768 fix: valuation rate for no Use Batch wise Valuation batches
(cherry picked from commit 4befa15198)
2026-03-17 08:42:59 +00:00
Sakthivel Murugan S
a5d1afe304 fix(minor): filter bank accounts in bank statement import (#53481) 2026-03-17 11:31:22 +05:30
Nikhil Kothari
a85aeb2f9b chore: resolve conflicts 2026-03-17 11:28:37 +05:30
rohitwaghchaure
158e290580 Merge pull request #53518 from frappe/mergify/bp/version-15-hotfix/pr-53513
fix: do not set valuation rate for invoice without update stock (backport #53513)
2026-03-17 11:23:55 +05:30
mergify[bot]
0e770c0bbd fix: add item_name to quick entry fields in Item doctype (backport #53530) (#53532)
Co-authored-by: Abdus Samad <120767334+Samad-11@users.noreply.github.com>
fix: add item_name to quick entry fields in Item doctype (#53530)
2026-03-17 05:42:21 +00:00
mergify[bot]
d262a65b00 fix: correct overlap detection in JobCard.has_overlap (backport #53473) (#53522)
Co-authored-by: Sanjesh-Raju <sanjesh@tridotstech.com>
Co-authored-by: Sanjesh <rsanjesh64@gmail.com>
Co-authored-by: Tridots Tech <info@tridotstech.com>
fix: correct overlap detection in JobCard.has_overlap (#53473)
2026-03-17 10:42:51 +05:30
Nikhil Kothari
ab9d960aa8 fix(banking): include paid purchase invoices in reports and bank clearance (#52675)
* fix(banking): include paid purchase invoices in reports and bank clearance

* fix: condition for amounts not reflected in system

* fix: set Sales Invoice to be the payment document in bank rec

* fix: add additional filter for `is_paid`

* fix: added is_paid

* fix: added invoice number in bank clearance tool

* chore: make requested changes

* fix: exclude opening JEs

* fix: bring back banking icon in desktop

(cherry picked from commit ef32622166)

# Conflicts:
#	erpnext/accounts/doctype/bank_clearance/bank_clearance.py
#	erpnext/desktop_icon/banking.json
2026-03-17 04:48:25 +00:00
mergify[bot]
eec8cf8a71 fix: change "Date" label to "Posting Date" in Sales Invoice and Purchase Invoice (backport #53503) (#53516)
* fix: change "Date" label to "Posting Date" in Sales Invoice and Purchase Invoice (#53503)

(cherry picked from commit 4cd150ba7a)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.json

* chore: resolve conflicts

* chore: resolve conflicts

---------

Co-authored-by: Abdus Samad <120767334+Samad-11@users.noreply.github.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-16 18:33:11 +00:00
Rohit Waghchaure
284ccd1def fix: do not set valuation rate for invoice without update stock
(cherry picked from commit bec9e48435)
2026-03-16 18:03:28 +00:00
rohitwaghchaure
3aafed0659 Merge pull request #53497 from frappe/mergify/bp/version-15-hotfix/pr-53495
fix: stock adjustment entry (backport #53495)
2026-03-16 18:07:13 +05:30
mergify[bot]
30fe711c44 fix(support-settings): disable the auto-close tickets feature if close_issue_after_days is set to 0 (backport #53499) (#53504)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(support-settings): disable the auto-close tickets feature if `close_issue_after_days` is set to 0 (#53499)
2026-03-16 12:12:04 +00:00
Rohit Waghchaure
ac6c06daf9 fix: stock adjustment entry
(cherry picked from commit af3067ee23)
2026-03-16 09:47:52 +00:00
mergify[bot]
b63b5320f2 fix(p&l_statement): disable accumulated value filter by default (backport #53488) (#53489)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(p&l_statement): disable accumulated value filter by default (#53488)
2026-03-16 12:53:12 +05:30
ruthra kumar
433dec8a6c Merge pull request #53418 from frappe/mergify/bp/version-15-hotfix/pr-53415
fix: broke cost center filter in get outstanding reference docs (backport #53415)
2026-03-16 10:09:03 +05:30
mergify[bot]
81244a84e7 chore: add docs to project URLs (backport #53467) (#53468)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2026-03-15 14:53:39 +01:00
mergify[bot]
7a7c4a03f0 fix(serial_and_batch_bundle_selector): handle CSV attachment properly (backport #53460) (#53461)
Co-authored-by: diptanilsaha <diptanil@frappe.io>
fix(serial_and_batch_bundle_selector): handle CSV attachment properly (#53460)
2026-03-15 07:45:00 +00:00
mergify[bot]
a6cf31edad fix: sales order indicator should be based on available qty rather th… (backport #53456) (#53457) 2026-03-15 09:46:07 +05:30
ruthra kumar
d6693c9b79 Merge pull request #53425 from frappe/mergify/bp/version-15-hotfix/pr-53423
refactor: disable total row in trends report (backport #53423)
2026-03-13 18:18:26 +05:30
ruthra kumar
56ffd52335 refactor: disable total row in trends report
(cherry picked from commit 4dbc72b301)
2026-03-13 12:32:55 +00:00
ruthra kumar
53e3bfbf22 fix: broke cost center filter in get outstanding reference docs
(cherry picked from commit 7dfe36fdce)
2026-03-13 09:55:16 +00:00
mergify[bot]
db9dc86694 Revert "fix(regional): rename duplicate Customer fields in Italy setup" (backport #53409) (#53410)
* Revert "fix(regional): rename duplicate Customer fields in Italy setup" (#53409)

(cherry picked from commit bd87a7e612)

# Conflicts:
#	erpnext/patches.txt

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-13 07:47:07 +00:00
Khushi Rawat
1c444ef822 Merge pull request #53407 from khushi8112/show-asset-purchase-amount-currency
fix: update label on company change
2026-03-13 12:42:02 +05:30
khushi8112
f702a71126 chore: linters check 2026-03-13 12:22:45 +05:30
khushi8112
908e185cfe fix: update label on company change 2026-03-13 11:59:42 +05:30
mergify[bot]
2a70203cab fix(regional): rename duplicate Customer fields in Italy setup (backport #50921) (#53397)
* fix(regional): rename duplicate Customer fields in Italy setup (#50921)

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit c6efc403cd)

# Conflicts:
#	erpnext/patches.txt

* chore: resolve conflicts

* chore: resolve conflicts

Removed obsolete patches for older versions.

---------

Co-authored-by: Solede <lorenzo.caldara@gmail.com>
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-12 15:20:22 +00:00
Mihir Kandoi
85c4cc3e1b fix: update delivery date in line items (#53331) 2026-03-12 20:37:52 +05:30
Khushi Rawat
62280c285f Merge pull request #53377 from frappe/asset-repair-show-general-ledger
fix: use completion_date not posting date
2026-03-12 15:10:04 +05:30
mergify[bot]
5737d2afa3 fix: precision issue in production plan (backport #53370) (#53373)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: precision issue in production plan (#53370)
2026-03-12 09:32:09 +00:00
mergify[bot]
0e00ab8865 fix: do not modify rate in the child item merely for comparison (backport #53301) (#53375)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: do not modify rate in the child item merely for comparison (#53301)
2026-03-12 09:20:39 +00:00
Khushi Rawat
05d614eb04 fix: coderebbit review 2026-03-12 14:41:23 +05:30
khushi8112
6d476604df fix: use completion_date not posting date 2026-03-12 14:24:59 +05:30
Ejaaz Khan
0d527ac8ea Merge pull request #53359 from frappe/mergify/bp/version-15-hotfix/pr-53348
fix: remove redundant pos print format (backport #53348)
2026-03-12 12:53:40 +05:30
mergify[bot]
0612f1c941 fix: NoneType error when template description is to be copied to variant (backport #53358) (#53365)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
fix: NoneType error when template description is to be copied to variant (#53358)
2026-03-12 06:49:53 +00:00
mergify[bot]
dbed426725 refactor: supplier quotation comparision report button should start f… (backport #53361) (#53362)
Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2026-03-12 06:19:37 +00:00
Ejaaz Khan
8497d1f8cf fix: remove redundant pos print format (#53348)
(cherry picked from commit e4d79c6246)
2026-03-12 06:02:34 +00:00
mergify[bot]
034d460ae1 fix(delivery note): avoid maintaining si_detail on return delivery note (backport #52456) (#53352)
Co-authored-by: NaviN <118178330+Navin-S-R@users.noreply.github.com>
fix(delivery note): avoid maintaining si_detail on return delivery note (#52456)
2026-03-12 05:40:33 +00:00
mergify[bot]
fd94cd0e7c Feat/shipment default contact (backport #53029) (#53354)
Co-authored-by: David <52141166+sdavidbastos@users.noreply.github.com>
2026-03-12 05:40:00 +00:00
mergify[bot]
db251c6e11 fix: re-calculate taxes and totals after resetting bundle item rate (backport #53342) (#53349)
Co-authored-by: V Shankar <shankarv292002@gmail.com>
fix: re-calculate taxes and totals after resetting bundle item rate (#53342)
2026-03-12 05:15:12 +00:00
Nihantra C. Patel
b037dae529 Merge pull request #53335 from frappe/mergify/bp/version-15-hotfix/pr-53327
fix: Append existing ignored doctypes in Journal Entry on_cancel instead of overwriting (backport #53327)
2026-03-11 16:29:11 +05:30
Nihantra Patel
b73d9700d0 fix: Append existing ignored doctypes in Journal Entry on_cancel instead of overwriting
(cherry picked from commit 39e10c4ab0)
2026-03-11 10:41:30 +00:00
ruthra kumar
73d347f456 Merge pull request #53328 from frappe/mergify/bp/version-15-hotfix/pr-53326
refactor: make cost center editable in payment entry deduction (backport #53326)
2026-03-11 15:08:34 +05:30
ruthra kumar
3e7d2c6f11 refactor: make cost center editable in payment entry deduction
(cherry picked from commit 078b22d985)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json
2026-03-11 14:53:09 +05:30
Nishka Gosalia
4ebc23752e Merge pull request #53324 from frappe/mergify/bp/version-15-hotfix/pr-53312
fix: update item description in Production Plan Assembly Items table (backport #53312)
2026-03-11 14:35:10 +05:30
Parameshwari Palanisamy
ef6fd7dcb5 Update production_plan.py
(cherry picked from commit 39e68a9ce7)
2026-03-11 08:49:06 +00:00
creative-paramu
e3e9d7b19e fix: update item description in Production Plan Assembly Items table
(cherry picked from commit 19533551f4)
2026-03-11 08:49:06 +00:00
NaviN
a4aaf67b2b Merge pull request #52964 from ljain112/fix-tds-party
fix(tds-report): correct party type filtering and refactor
2026-03-11 11:41:44 +05:30
Jatin3128
d425e90ef7 Merge pull request #53213 from Jatin3128/fix-53173
fix: correct payment terms fetching and recalculation logic
2026-03-11 02:56:03 +05:30
Jatin3128
79b04826d9 fix: correct payment terms fetching and recalculation logic 2026-03-11 02:55:40 +05:30
NaviN
8c7100df04 Merge pull request #53279 from frappe/mergify/bp/version-15-hotfix/pr-53203
fix: update user status depends on employee status (backport #53203)
2026-03-10 22:41:20 +05:30
Frappe PR Bot
1ffd814f92 chore(release): Bumped to Version 15.101.0
# [15.101.0](https://github.com/frappe/erpnext/compare/v15.100.2...v15.101.0) (2026-03-10)

### Bug Fixes

* **accounts:** compute tax net_amount in JS controller ([6ad84d6](6ad84d66cc))
* **accounts:** round and convert net_amount to company currency in JS tax controller ([516ad90](516ad9021b))
* balance qty for inv dimension ([6898d70](6898d70382))
* better validation message for Purchase Invoice with Update Stock ([b7fd9ae](b7fd9aea6a))
* client-side taxes calculation ([#44510](https://github.com/frappe/erpnext/issues/44510)) ([717c5b2](717c5b25eb)), closes [#44328](https://github.com/frappe/erpnext/issues/44328)
* correct logic for repair cost in asset repair ([c71557f](c71557f432))
* disallow all actions on job card if work order is closed ([7b2e483](7b2e4832aa))
* enforce permission check for purchase invoice and update test to use service expense account ([a6dd078](a6dd07802a))
* **gross-profit:** apply precision-based rounding to grouped totals ([b59dc17](b59dc173b8))
* **help:** escape query (backport [#53192](https://github.com/frappe/erpnext/issues/53192)) ([#53194](https://github.com/frappe/erpnext/issues/53194)) ([ba4a99b](ba4a99b22c))
* **manufacturing:** ignore sales order validation for subassembly item ([624d1d4](624d1d4759))
* **manufacturing:** show returned qty in progress bar ([260d87a](260d87a80c))
* removed non existent patch ([fd8fac7](fd8fac7d40))
* **selling:** update delivery date in line items ([dfbb3e9](dfbb3e97a8))
* set default repair cost to 0 if no value is returned ([0b1746a](0b1746a4c8))
* skip asset sale processing for internal transfer invoices ([a7e8f31](a7e8f31f56))
* stock balance report qty ([180e232](180e232eb0))
* **test:** ensure warehouse is consistently referenced in asset repair tests ([ed428ce](ed428ceb1c))
* **test:** include warehouse parameter in asset repair test case ([bcc542b](bcc542b1f9))
* updating costing based on employee change in timesheet ([be59810](be598108b6))
* validation for cancellation ([c142a2b](c142a2be9c))

### Features

* allowing rate modification in update item in quotation (backport [#53147](https://github.com/frappe/erpnext/issues/53147)) ([#53150](https://github.com/frappe/erpnext/issues/53150)) ([072ab8d](072ab8d5f3))
* **manufacturing:** show disassembled qty in progress bar ([c572a01](c572a019b4))
2026-03-10 14:48:03 +00:00
ruthra kumar
c6e7cf13b5 Merge pull request #53293 from frappe/version-15-hotfix
chore: release v15
2026-03-10 20:16:23 +05:30
Navin-S-R
55a0603356 chore: resolve conflict 2026-03-10 17:58:57 +05:30
ruthra kumar
abe433cfa7 Merge pull request #53296 from frappe/mergify/bp/version-15-hotfix/pr-53071
fix(gross-profit): apply precision-based rounding to grouped totals (backport #53071)
2026-03-10 16:55:44 +05:30
Khushi Rawat
49648b5c6e Merge pull request #53258 from ljain112/backport-50804
fix: correct logic for repair cost in asset repair
2026-03-10 16:46:53 +05:30
Navin-S-R
b59dc173b8 fix(gross-profit): apply precision-based rounding to grouped totals
(cherry picked from commit 52dd7665e7)
2026-03-10 11:08:07 +00:00
rohitwaghchaure
7454db2b3e Merge pull request #53288 from frappe/mergify/bp/version-15-hotfix/pr-53283
fix: removed non existent patch (backport #53283)
2026-03-10 14:48:11 +05:30
mergify[bot]
fcfadf9dea Merge pull request #53286 from frappe/mergify/bp/version-15-hotfix/pr-53282
fix: allow user to make QI after submission not working (backport #53282)
2026-03-10 09:12:20 +00:00
rohitwaghchaure
098f6fd0d2 Merge pull request #53284 from frappe/mergify/bp/version-15-hotfix/pr-53281
fix: better validation message for Purchase Invoice with Update Stock (backport #53281)
2026-03-10 14:29:44 +05:30
Rohit Waghchaure
fd8fac7d40 fix: removed non existent patch
(cherry picked from commit c4b3080eae)
2026-03-10 08:56:16 +00:00
Rohit Waghchaure
b7fd9aea6a fix: better validation message for Purchase Invoice with Update Stock
(cherry picked from commit cfb06cf247)
2026-03-10 08:33:50 +00:00
Poovitha Palanivelu
c5796fed4a fix: update user status depends on employee status
(cherry picked from commit 194d060f13)

# Conflicts:
#	erpnext/setup/doctype/employee/employee.py
2026-03-10 07:39:56 +00:00
Mihir Kandoi
2b25059315 Merge pull request #53276 from frappe/mergify/bp/version-15-hotfix/pr-53235
fix: update item row delivery dates when header delivery date changes in sales order (backport #53235)
2026-03-10 13:09:31 +05:30
Pandiyan37
dfbb3e97a8 fix(selling): update delivery date in line items
(cherry picked from commit 77367b5517)
2026-03-10 07:23:38 +00:00
ruthra kumar
ee22347d64 Merge pull request #53243 from frappe/mergify/bp/version-15-hotfix/pr-53239
fix: validation for cancellation (backport #53239)
2026-03-10 12:23:14 +05:30
ruthra kumar
4d418d40db Merge pull request #52995 from frappe/mergify/bp/version-15-hotfix/pr-52630
fix(account): compute tax net_amount in JS controller (backport #52630)
2026-03-09 20:26:44 +05:30
ljain112
a6dd07802a fix: enforce permission check for purchase invoice and update test to use service expense account 2026-03-09 19:53:57 +05:30
ljain112
94972da845 fix: correct function syntax in TDS Computation Report 2026-03-09 19:25:38 +05:30
ljain112
c2e67599f5 fix: refactor GL entry mapping to include voucher type 2026-03-09 19:14:21 +05:30
ljain112
bcc542b1f9 fix(test): include warehouse parameter in asset repair test case 2026-03-09 19:11:02 +05:30
ljain112
ed428ceb1c fix(test): ensure warehouse is consistently referenced in asset repair tests 2026-03-09 18:54:43 +05:30
ljain112
93ebec90ef fix: enhance sorting and optimize GL entry retrieval 2026-03-09 18:50:42 +05:30
ljain112
0b1746a4c8 fix: set default repair cost to 0 if no value is returned 2026-03-09 18:34:46 +05:30
ljain112
c71557f432 fix: correct logic for repair cost in asset repair 2026-03-09 18:03:59 +05:30
Rohit Waghchaure
c142a2be9c fix: validation for cancellation
(cherry picked from commit 8de272a8a1)
2026-03-09 07:49:22 +00:00
Mihir Kandoi
3c77653508 Merge pull request #53240 from frappe/mergify/bp/version-15-hotfix/pr-53234
fix(manufacturing): show returned qty in progress bar (backport #53234)
2026-03-09 12:42:04 +05:30
Sudharsanan11
c572a019b4 feat(manufacturing): show disassembled qty in progress bar
(cherry picked from commit ae9ff767fa)
2026-03-09 07:04:32 +00:00
Sudharsanan11
260d87a80c fix(manufacturing): show returned qty in progress bar
(cherry picked from commit 8027f5aafd)
2026-03-09 07:04:31 +00:00
ruthra kumar
e0f5ae2d4c Merge pull request #53228 from frappe/mergify/bp/version-15-hotfix/pr-53227
refactor: party type and party filter for comparison report (backport #53227)
2026-03-08 05:49:51 +05:30
ruthra kumar
37e750e877 refactor: party type and party filter for comparison report
(cherry picked from commit b6f9c0844e)
2026-03-07 12:49:44 +00:00
Jatin3128
3148816451 fix: correct payment terms fetching and recalculation logic 2026-03-06 15:08:35 +05:30
rohitwaghchaure
5764f5ec80 Merge pull request #53207 from frappe/mergify/bp/version-15-hotfix/pr-53200
fix: stock balance report qty (backport #53200)
2026-03-06 13:08:11 +05:30
mergify[bot]
e9ae156323 refactor: use postprocess in mapped_doc to update items in subcontracting controller (backport #52724) (#52936)
* refactor: use postprocess in mapped_doc to update items in subcontracting controller

(cherry picked from commit 1d3d09f48c)

# Conflicts:
#	erpnext/controllers/subcontracting_controller.py

* chore: resolve conflicts

* chore: resolve conflicts

---------

Co-authored-by: ljain112 <ljain112@gmail.com>
2026-03-06 12:59:15 +05:30
rohitwaghchaure
54fdce648e chore: fix conflicts 2026-03-06 12:47:18 +05:30
Rohit Waghchaure
180e232eb0 fix: stock balance report qty
(cherry picked from commit a15e5fdc4e)

# Conflicts:
#	erpnext/stock/report/stock_balance/stock_balance.py
2026-03-06 07:15:30 +00:00
mergify[bot]
ba4a99b22c fix(help): escape query (backport #53192) (#53194)
fix(help): escape query (#53192)


(cherry picked from commit 702adda000)

Signed-off-by: Akhil Narang <me@akhilnarang.dev>
Co-authored-by: Akhil Narang <me@akhilnarang.dev>
2026-03-05 18:40:49 +05:30
Mihir Kandoi
9100428f1b Merge pull request #53176 from frappe/resolve/version-15-hotfix/pr-53084 2026-03-05 17:36:47 +05:30
rohitwaghchaure
1950e82d1e Merge pull request #53180 from frappe/mergify/bp/version-15-hotfix/pr-52745
fix: balance qty for inv dimension (backport #52745)
2026-03-05 14:27:27 +05:30
Rohit Waghchaure
6898d70382 fix: balance qty for inv dimension
(cherry picked from commit a3eafe5b18)
2026-03-05 08:20:02 +00:00
Sudharsanan11
624d1d4759 fix(manufacturing): ignore sales order validation for subassembly item
(cherry picked from commit 6b1aac4aee)
2026-03-05 12:50:27 +05:30
Mihir Kandoi
bf6b5b7b7f Merge pull request #53164 from frappe/mergify/bp/version-15-hotfix/pr-53157
fix: disallow all actions on job card if work order is closed (backport #53157)
2026-03-04 17:09:17 +05:30
Khushi Rawat
24d9e2c5a9 Merge pull request #53162 from frappe/mergify/bp/version-15-hotfix/pr-53154
fix: skip asset sale processing for internal transfer invoices (backport #53154)
2026-03-04 17:00:56 +05:30
Mihir Kandoi
7b2e4832aa fix: disallow all actions on job card if work order is closed
(cherry picked from commit ee19c32c3a)
2026-03-04 11:21:34 +00:00
khushi8112
a7e8f31f56 fix: skip asset sale processing for internal transfer invoices
(cherry picked from commit 9cb3dad079)
2026-03-04 11:07:05 +00:00
Nishka Gosalia
d5a250a254 Merge pull request #53158 from frappe/mergify/bp/version-15-hotfix/pr-53156
fix: updating costing based on employee change in timesheet (backport #53156)
2026-03-04 16:35:06 +05:30
Nishka Gosalia
be598108b6 fix: updating costing based on employee change in timesheet
(cherry picked from commit e37d4a6f7c)
2026-03-04 10:42:50 +00:00
mergify[bot]
68bac20198 Merge pull request #53095 from frappe/mergify/bp/version-15-hotfix/pr-52838
fix: correct fields being updated on material request and purchase or… (backport #52838)
2026-03-04 07:33:02 +00:00
mergify[bot]
072ab8d5f3 feat: allowing rate modification in update item in quotation (backport #53147) (#53150)
Co-authored-by: Nishka Gosalia <nishkagosalia@Nishkas-MacBook-Air.local>
2026-03-04 07:29:46 +00:00
David Arnold
717c5b25eb fix: client-side taxes calculation (#44510)
closes: #44328
2026-02-26 13:55:17 +05:30
ruthra kumar
e5282a48ae chore: resolve conflicts 2026-02-26 13:52:23 +05:30
Luis Mendoza
516ad9021b fix(accounts): round and convert net_amount to company currency in JS tax controller
(cherry picked from commit b10b205394)

# Conflicts:
#	erpnext/public/js/controllers/taxes_and_totals.js
2026-02-26 08:17:52 +00:00
Luis Mendoza
86c628521e style: prettier formatting
(cherry picked from commit 485166b668)
2026-02-26 08:17:51 +00:00
Luis Mendoza
6ad84d66cc fix(accounts): compute tax net_amount in JS controller
(cherry picked from commit 153ad99f85)

# Conflicts:
#	erpnext/public/js/controllers/taxes_and_totals.js
2026-02-26 08:17:51 +00:00
ljain112
e5eb5406da fix(tds-report): correct party type filtering and refactor 2026-02-25 16:30:30 +05:30
369 changed files with 13760 additions and 5508 deletions

10
.greptile/config.json Normal file
View File

@@ -0,0 +1,10 @@
{
"disabledLabels": [
"conflicts"
],
"context": {
"repos": [
"frappe/frappe"
]
}
}

View File

@@ -4,7 +4,7 @@ import inspect
import frappe
from frappe.utils.user import is_website_user
__version__ = "15.100.2"
__version__ = "15.112.0"
def get_default_company(user=None):

View File

@@ -1,126 +0,0 @@
{
"custom_fields": [
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"creation": "2018-12-28 22:29:21.828090",
"default": null,
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Address",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "tax_category",
"fieldtype": "Link",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"idx": 15,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "fax",
"label": "Tax Category",
"length": 0,
"mandatory_depends_on": null,
"modified": "2018-12-28 22:29:21.828090",
"modified_by": "Administrator",
"name": "Address-tax_category",
"no_copy": 0,
"options": "Tax Category",
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
},
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"creation": "2020-10-14 17:41:40.878179",
"default": "0",
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Address",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "is_your_company_address",
"fieldtype": "Check",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"idx": 20,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "linked_with",
"label": "Is Your Company Address",
"length": 0,
"mandatory_depends_on": null,
"modified": "2020-10-14 17:41:40.878179",
"modified_by": "Administrator",
"name": "Address-is_your_company_address",
"no_copy": 0,
"options": null,
"owner": "Administrator",
"parent": null,
"parentfield": null,
"parenttype": null,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0,
"width": null
}
],
"custom_perms": [],
"doctype": "Address",
"property_setters": [],
"sync_on_migrate": 1
}

View File

@@ -517,6 +517,7 @@ def get_account_autoname(account_number, account_name, company):
def update_account_number(name, account_name, account_number=None, from_descendant=False):
_ensure_idle_system()
account = frappe.get_cached_doc("Account", name)
account.check_permission("write")
if not account:
return
@@ -578,10 +579,12 @@ def update_account_number(name, account_name, account_number=None, from_descenda
@frappe.whitelist()
def merge_account(old, new):
_ensure_idle_system()
# Validate properties before merging
new_account = frappe.get_cached_doc("Account", new)
old_account = frappe.get_cached_doc("Account", old)
new_account.check_permission("write")
old_account.check_permission("write")
if not new_account:
throw(_("Account {0} does not exist").format(new))

View File

@@ -34,6 +34,13 @@
"account_number": "0430",
"account_type": "Fixed Asset"
},
"Anlagen im Bau": {
"is_group": 1,
"Andere Anlagen, Betriebs- und Geschäftsausstattung im Bau": {
"account_number": "0498",
"account_type": "Capital Work in Progress"
}
},
"Accumulated Depreciation": {
"account_type": "Accumulated Depreciation"
}
@@ -317,13 +324,21 @@
"account_number": "3800",
"account_type": "Expenses Included In Asset Valuation"
},
"Bestandsveränderungen Roh-, Hilfs- und Betriebsstoffe sowie bezogene Waren": {
"account_number": "3960",
"account_type": "Stock Adjustment"
},
"Herstellungskosten": {
"account_number": "4996",
"account_type": "Cost of Goods Sold"
},
"Anlagenabgänge Sachanlagen (Restbuchwert bei Buchverlust)": {
"account_number": "2310",
"account_type": "Expense Account"
},
"Verluste aus dem Abgang von Gegenständen des Anlagevermögens": {
"account_number": "2320",
"account_type": "Stock Adjustment"
"account_type": "Expense Account"
},
"Verwaltungskosten": {
"account_number": "4997",
@@ -340,7 +355,7 @@
"is_group": 1,
"Abschreibungen auf Sachanlagen (ohne AfA auf Kfz und Gebäude)": {
"account_number": "4830",
"account_type": "Accumulated Depreciation"
"account_type": "Depreciation"
},
"Abschreibungen auf Gebäude": {
"account_number": "4831",

View File

@@ -0,0 +1,449 @@
{
"country_code": "nz",
"name": "New Zealand - Chart of Accounts with Account Numbers",
"disabled": "No",
"tree": {
"Application of Funds (Assets)": {
"Current Assets": {
"Bank Accounts": {
"Business Transaction Account": {
"account_number": "11011",
"account_type": "Bank"
},
"Business Savings Account": {
"account_number": "11012",
"account_type": "Bank"
},
"account_number": "11010",
"is_group": 1
},
"Cash on Hand": {
"account_number": "11020",
"account_type": "Cash"
},
"Accounts Receivable": {
"Debtors": {
"account_number": "11210",
"account_type": "Receivable"
},
"Provision for Doubtful Debts": {
"account_number": "11220"
},
"account_number": "11200",
"is_group": 1
},
"Inventory": {
"Stock on Hand": {
"account_number": "11311",
"account_type": "Stock"
},
"Work In Progress": {
"account_number": "11312",
"account_type": "Stock"
},
"account_number": "11310",
"account_type": "Stock",
"is_group": 1
},
"Prepayments": {
"Prepayments": {
"account_number": "11411"
},
"Supplier Advances": {
"account_number": "11412"
},
"Deferred Expense": {
"account_number": "11413"
},
"account_number": "11410",
"is_group": 1
},
"GST Receivable": {
"account_number": "11510",
"account_type": "Tax"
},
"Income Tax Receivable": {
"account_number": "11520",
"account_type": "Tax"
},
"account_number": "11000",
"is_group": 1
},
"Fixed Assets": {
"Plant & Equipment": {
"Plant & Equipment": {
"account_number": "16011",
"account_type": "Fixed Asset"
},
"Accumulated Depreciation - Plant & Equipment": {
"account_number": "16012",
"account_type": "Accumulated Depreciation"
},
"account_number": "16010",
"is_group": 1
},
"Motor Vehicles": {
"Motor Vehicles": {
"account_number": "16021",
"account_type": "Fixed Asset"
},
"Accumulated Depreciation - Motor Vehicles": {
"account_number": "16022",
"account_type": "Accumulated Depreciation"
},
"account_number": "16020",
"is_group": 1
},
"Office Equipment": {
"Office Equipment": {
"account_number": "16031",
"account_type": "Fixed Asset"
},
"Accumulated Depreciation - Office Equipment": {
"account_number": "16032",
"account_type": "Accumulated Depreciation"
},
"account_number": "16030",
"is_group": 1
},
"Buildings": {
"Buildings": {
"account_number": "16041",
"account_type": "Fixed Asset"
},
"Accumulated Depreciation - Buildings": {
"account_number": "16042",
"account_type": "Accumulated Depreciation"
},
"account_number": "16040",
"is_group": 1
},
"Computer Equipment": {
"Computer Equipment": {
"account_number": "16051",
"account_type": "Fixed Asset"
},
"Accumulated Depreciation - Computer Equipment": {
"account_number": "16052",
"account_type": "Accumulated Depreciation"
},
"account_number": "16050",
"is_group": 1
},
"Capital Work in Progress": {
"account_number": "16090",
"account_type": "Capital Work in Progress"
},
"account_number": "16000",
"is_group": 1
},
"account_number": "10000",
"root_type": "Asset"
},
"Source of Funds (Liabilities)": {
"Current Liabilities": {
"Accounts Payable": {
"Creditors": {
"account_number": "21010",
"account_type": "Payable"
},
"account_number": "21000",
"is_group": 1
},
"Goods Received Not Invoiced": {
"account_number": "21100",
"account_type": "Stock Received But Not Billed"
},
"Asset Received Not Invoiced": {
"account_number": "21110",
"account_type": "Asset Received But Not Billed"
},
"Service Received Not Invoiced": {
"account_number": "21120",
"account_type": "Service Received But Not Billed"
},
"Accrued Expenses": {
"account_number": "21200"
},
"Wages Payable": {
"account_number": "21300"
},
"PAYE Payable": {
"account_number": "22010"
},
"KiwiSaver Payable": {
"account_number": "22020"
},
"ACC Payable": {
"account_number": "22030"
},
"Credit Cards": {
"Business Credit Card": {
"account_number": "22110"
},
"account_number": "22100",
"is_group": 1
},
"Customer Advances": {
"account_number": "22200"
},
"Deferred Revenue": {
"account_number": "22210"
},
"Provisional Account": {
"account_number": "22220"
},
"Tax Liabilities": {
"GST Payable": {
"account_number": "22310",
"account_type": "Tax"
},
"GST Suspense": {
"account_number": "22320",
"account_type": "Tax"
},
"FBT Payable": {
"account_number": "22330",
"account_type": "Tax"
},
"Income Tax Payable": {
"account_number": "22340",
"account_type": "Tax"
},
"account_number": "22300",
"is_group": 1
},
"account_number": "21500",
"is_group": 1
},
"Non-Current Liabilities": {
"Bank Loans": {
"Bank Loan": {
"account_number": "25011"
},
"account_number": "25010",
"is_group": 1
},
"Lease Liabilities": {
"Lease Liability": {
"account_number": "25021"
},
"account_number": "25020",
"is_group": 1
},
"Shareholder Loans": {
"Shareholder Loan": {
"account_number": "25031"
},
"account_number": "25030",
"is_group": 1
},
"account_number": "25000",
"is_group": 1
},
"account_number": "20000",
"root_type": "Liability"
},
"Equity": {
"Share Capital": {
"account_number": "31010",
"account_type": "Equity"
},
"Drawings": {
"account_number": "31020",
"account_type": "Equity"
},
"Current Year Earnings": {
"account_number": "35010",
"account_type": "Equity"
},
"Retained Earnings": {
"account_number": "35020",
"account_type": "Equity"
},
"account_number": "30000",
"root_type": "Equity"
},
"Income": {
"Sales": {
"account_number": "41010",
"account_type": "Income Account"
},
"Other Income": {
"Interest Income": {
"account_number": "47010",
"account_type": "Income Account"
},
"Rounding Gain/Loss": {
"account_number": "47020",
"account_type": "Income Account"
},
"Foreign Exchange Gain": {
"account_number": "47030",
"account_type": "Income Account"
},
"account_number": "47000",
"is_group": 1
},
"account_number": "40000",
"root_type": "Income"
},
"Expenses": {
"Cost of Goods Sold": {
"Purchases": {
"account_number": "51010",
"account_type": "Cost of Goods Sold"
},
"Freight Inwards": {
"account_number": "51020",
"account_type": "Expenses Included In Valuation"
},
"Duty and Landing Costs": {
"account_number": "51030",
"account_type": "Expenses Included In Valuation"
},
"Stock Adjustment": {
"account_number": "51040",
"account_type": "Stock Adjustment"
},
"Stock Write Off": {
"account_number": "51050",
"account_type": "Stock Adjustment"
},
"account_number": "51000",
"account_type": "Cost of Goods Sold",
"is_group": 1
},
"Operating Expenses": {
"Wages & Salaries": {
"account_number": "61010",
"account_type": "Expense Account"
},
"KiwiSaver Employer Contribution": {
"account_number": "61020",
"account_type": "Expense Account"
},
"ACC Levies": {
"account_number": "61030",
"account_type": "Expense Account"
},
"Rent": {
"account_number": "65010",
"account_type": "Expense Account"
},
"Power": {
"account_number": "65020",
"account_type": "Expense Account"
},
"Telephone": {
"account_number": "66010",
"account_type": "Expense Account"
},
"Insurance": {
"account_number": "64010",
"account_type": "Expense Account"
},
"Accounting Fees": {
"account_number": "64020",
"account_type": "Expense Account"
},
"Legal Fees": {
"account_number": "64030",
"account_type": "Expense Account"
},
"Advertising and Marketing": {
"account_number": "65030",
"account_type": "Expense Account"
},
"Repairs and Maintenance": {
"account_number": "65040",
"account_type": "Expense Account"
},
"Freight and Courier": {
"account_number": "65050",
"account_type": "Expense Account"
},
"Operating Costs": {
"account_number": "65060",
"account_type": "Expense Account"
},
"account_number": "60000",
"is_group": 1
},
"Depreciation and Amortisation": {
"Depreciation - Plant & Equipment": {
"account_number": "62010",
"account_type": "Depreciation"
},
"Depreciation - Motor Vehicles": {
"account_number": "62020",
"account_type": "Depreciation"
},
"Depreciation - Office Equipment": {
"account_number": "62030",
"account_type": "Depreciation"
},
"Depreciation - Computer Equipment": {
"account_number": "62040",
"account_type": "Depreciation"
},
"account_number": "62000",
"is_group": 1
},
"Finance Costs": {
"Bank Charges": {
"account_number": "67010",
"account_type": "Expense Account"
},
"Interest Expense": {
"account_number": "67020",
"account_type": "Expense Account"
},
"Rounding Off": {
"account_number": "67030",
"account_type": "Round Off"
},
"Payment Discounts": {
"account_number": "67040",
"account_type": "Expense Account"
},
"account_number": "67000",
"is_group": 1
},
"Income Tax Expense": {
"account_number": "81010",
"account_type": "Expense Account"
},
"Foreign Exchange": {
"Exchange Gain/Loss": {
"account_number": "82010",
"account_type": "Expense Account"
},
"Unrealized Exchange Gain/Loss": {
"account_number": "82020",
"account_type": "Expense Account"
},
"account_number": "82000",
"is_group": 1
},
"Bad Debts": {
"account_number": "83010",
"account_type": "Expense Account"
},
"Write Off": {
"account_number": "83020",
"account_type": "Expense Account"
},
"Gain/Loss on Asset Disposal": {
"account_number": "83030",
"account_type": "Expense Account"
},
"Expenses Included In Asset Valuation": {
"account_number": "84010",
"account_type": "Expenses Included In Asset Valuation"
},
"account_number": "50000",
"root_type": "Expense"
}
}
}

View File

@@ -0,0 +1,840 @@
{
"name": "Philippines",
"country": "Philippines",
"tree": {
"Asset": {
"account_number": "1000",
"is_group": 1,
"root_type": "Asset",
"Current Assets": {
"account_number": "1001",
"is_group": 1,
"root_type": "Asset",
"Cash": {
"account_number": "1100",
"is_group": 1,
"root_type": "Asset",
"account_type": "Cash",
"Cash on Hand": {
"account_number": "1101",
"is_group": 0,
"root_type": "Asset",
"account_type": "Cash"
},
"Petty Cash Fund": {
"account_number": "1200",
"is_group": 1,
"root_type": "Asset",
"account_type": "Cash",
"Petty Cash Fund": {
"account_number": "1201",
"is_group": 0,
"root_type": "Asset",
"account_type": "Cash"
}
}
},
"Bank Accounts": {
"account_number": "1102",
"is_group": 1,
"root_type": "Asset",
"account_type": "Bank"
},
"Advances to Officers & Employees": {
"account_number": "1290",
"is_group": 1,
"root_type": "Asset",
"Advances to Officers & Employees": {
"account_number": "1291",
"is_group": 0,
"root_type": "Asset"
}
},
"Accounts Receivable Trade": {
"account_number": "1300",
"is_group": 1,
"root_type": "Asset",
"Accounts Receivable - Trade": {
"account_number": "1301",
"is_group": 0,
"root_type": "Asset",
"account_type": "Receivable"
}
},
"Accounts Receivable - Affiliates": {
"account_number": "1310",
"is_group": 1,
"root_type": "Asset",
"Due from Company": {
"account_number": "1311",
"is_group": 0,
"root_type": "Asset"
}
},
"Accounts Receivable - Others": {
"account_number": "1400",
"is_group": 1,
"root_type": "Asset",
"Accounts Receivable - Others": {
"account_number": "1401",
"is_group": 0,
"root_type": "Asset"
}
},
"Parts, Materials and Supplies": {
"account_number": "1500",
"is_group": 1,
"root_type": "Asset",
"Parts, Materials and Supplies": {
"account_number": "1501",
"is_group": 0,
"root_type": "Asset"
},
"Raw Materials - Demo": {
"account_number": "1502",
"is_group": 0,
"root_type": "Asset"
}
},
"Project in Progress": {
"account_number": "1510",
"is_group": 1,
"root_type": "Asset",
"Project in Progress": {
"account_number": "1511",
"is_group": 0,
"root_type": "Asset"
},
"Factory Overhead Variance": {
"account_number": "1512",
"is_group": 0,
"root_type": "Asset"
}
},
"Finished Goods": {
"account_number": "1520",
"is_group": 1,
"root_type": "Asset",
"Finished Goods Inventory": {
"account_number": "1531",
"is_group": 0,
"root_type": "Asset",
"account_type": "Stock"
},
"Inventory in Transit": {
"account_number": "1532",
"is_group": 0,
"root_type": "Asset",
"account_type": "Stock Adjustment"
}
},
"Prepayments": {
"account_number": "1600",
"is_group": 1,
"root_type": "Asset",
"Prepaid Insurance & Bonds": {
"account_number": "1601",
"is_group": 0,
"root_type": "Asset"
},
"Prepaid Rent": {
"account_number": "1602",
"is_group": 0,
"root_type": "Asset"
}
},
"VAT Input Tax": {
"account_number": "1610",
"is_group": 1,
"root_type": "Asset",
"VAT Input Tax - Goods": {
"account_number": "1611",
"is_group": 0,
"root_type": "Asset",
"account_type": "Tax"
}
}
},
"Non - Current Assets": {
"account_number": "1002",
"is_group": 1,
"root_type": "Asset",
"Property, Plants And Equipments": {
"account_number": "1700",
"is_group": 1,
"root_type": "Asset",
"Land": {
"account_number": "1701",
"is_group": 0,
"root_type": "Asset",
"account_type": "Fixed Asset"
},
"Buildings & Improvements": {
"account_number": "1702",
"is_group": 0,
"root_type": "Asset",
"account_type": "Fixed Asset"
},
"Delivery & Trans Equipment": {
"account_number": "1703",
"is_group": 0,
"root_type": "Asset",
"account_type": "Fixed Asset"
},
"Furniture & Fixtures": {
"account_number": "1704",
"is_group": 0,
"root_type": "Asset",
"account_type": "Fixed Asset"
},
"Machinery & Equipment": {
"account_number": "1705",
"is_group": 0,
"root_type": "Asset",
"account_type": "Fixed Asset"
}
},
"Accum Depr. - Property, Plants and Equipment": {
"account_number": "1800",
"is_group": 1,
"root_type": "Asset",
"Accumulated Dep Bdgs & Improv": {
"account_number": "1801",
"is_group": 0,
"root_type": "Asset",
"account_type": "Accumulated Depreciation"
},
"Accumulated Dep Delivery & Trans": {
"account_number": "1802",
"is_group": 0,
"root_type": "Asset",
"account_type": "Accumulated Depreciation"
},
"Accumulated Dep Furniture & Fixture": {
"account_number": "1803",
"is_group": 0,
"root_type": "Asset",
"account_type": "Accumulated Depreciation"
},
"Accumulated Depreciation - Machinery & Equipment": {
"account_number": "1804",
"is_group": 0,
"root_type": "Asset",
"account_type": "Accumulated Depreciation"
}
}
},
"Other Assets": {
"account_number": "1003",
"is_group": 1,
"root_type": "Asset",
"Advances To Supplier": {
"account_number": "1900",
"is_group": 1,
"root_type": "Asset",
"Advances To Supplier": {
"account_number": "1901",
"is_group": 0,
"root_type": "Asset"
}
},
"Miscellaneous Deposits": {
"account_number": "1910",
"is_group": 1,
"root_type": "Asset",
"Miscellaneous Deposits": {
"account_number": "1911",
"is_group": 0,
"root_type": "Asset"
}
},
"Retirement Fund": {
"account_number": "1920",
"is_group": 1,
"root_type": "Asset",
"Retirement Fund": {
"account_number": "1921",
"is_group": 0,
"root_type": "Asset"
}
},
"Investment": {
"account_number": "1930",
"is_group": 1,
"root_type": "Asset",
"Investment": {
"account_number": "1931",
"is_group": 0,
"root_type": "Asset"
}
},
"System Development": {
"account_number": "1940",
"is_group": 1,
"root_type": "Asset",
"System Development": {
"account_number": "1941",
"is_group": 0,
"root_type": "Asset"
}
}
}
},
"Liability": {
"account_number": "2000",
"is_group": 1,
"root_type": "Liability",
"Current Liabilities": {
"account_number": "2001",
"is_group": 1,
"root_type": "Liability",
"Accounts Payable Trade": {
"account_number": "2100",
"is_group": 1,
"root_type": "Liability",
"Accounts Payable - Trade": {
"account_number": "2101",
"is_group": 0,
"root_type": "Liability",
"account_type": "Payable"
}
},
"Accounts Payable Others": {
"account_number": "2110",
"is_group": 1,
"root_type": "Liability",
"Accounts Payable - Payroll": {
"account_number": "2111",
"is_group": 0,
"root_type": "Liability"
}
},
"VAT Output Tax": {
"account_number": "2200",
"is_group": 1,
"root_type": "Liability",
"VAT Output Tax": {
"account_number": "2201",
"is_group": 0,
"root_type": "Liability",
"account_type": "Tax"
}
},
"Withholding Taxes Payable Wages": {
"account_number": "2210",
"is_group": 1,
"root_type": "Liability",
"Withholding Taxes Payable Wages": {
"account_number": "2211",
"is_group": 0,
"root_type": "Liability",
"account_type": "Tax"
}
},
"Withholding Taxes Payable Expanded": {
"account_number": "2220",
"is_group": 1,
"root_type": "Liability",
"Withholding Taxes Payable Expanded": {
"account_number": "2221",
"is_group": 0,
"root_type": "Liability",
"account_type": "Tax"
}
},
"Accruals And Other Current Payables": {
"account_number": "2300",
"is_group": 1,
"root_type": "Liability",
"Stock Received But Not Billed": {
"account_number": "2301",
"is_group": 0,
"root_type": "Liability",
"account_type": "Stock Received But Not Billed"
}
},
"Payable to Government and Other Institutions": {
"account_number": "2400",
"is_group": 1,
"root_type": "Liability",
"SSS Premium Payable": {
"account_number": "2401",
"is_group": 0,
"root_type": "Liability"
},
"SSS Salary Loan Payable": {
"account_number": "2402",
"is_group": 0,
"root_type": "Liability"
},
"PhilHealth Premium": {
"account_number": "2403",
"is_group": 0,
"root_type": "Liability"
},
"Pag-ibig Loan Payable": {
"account_number": "2404",
"is_group": 0,
"root_type": "Liability"
},
"Coop Loans": {
"account_number": "2405",
"is_group": 0,
"root_type": "Liability"
},
"Coop Contributions": {
"account_number": "2406",
"is_group": 0,
"root_type": "Liability"
},
"Canteen": {
"account_number": "2407",
"is_group": 0,
"root_type": "Liability"
},
"AUB Loan Payable": {
"account_number": "2408",
"is_group": 0,
"root_type": "Liability"
},
"HSBC Loan Payable": {
"account_number": "2409",
"is_group": 0,
"root_type": "Liability"
}
},
"Customer Deposits": {
"account_number": "2500",
"is_group": 0,
"root_type": "Liability",
"account_type": "Payable"
}
},
"Non Current Liabilities": {
"account_number": "2002",
"is_group": 1,
"root_type": "Liability",
"Due To Associated Company": {
"account_number": "2600",
"is_group": 1,
"root_type": "Liability",
"Due To Associated Company": {
"account_number": "2601",
"is_group": 0,
"root_type": "Liability"
}
},
"Deferred Income": {
"account_number": "2700",
"is_group": 1,
"root_type": "Liability",
"Deferred Income": {
"account_number": "2701",
"is_group": 0,
"root_type": "Liability"
}
},
"Notes Payable": {
"account_number": "2800",
"is_group": 1,
"root_type": "Liability",
"Notes Payable": {
"account_number": "2801",
"is_group": 0,
"root_type": "Liability"
}
},
"Dividends Payable": {
"account_number": "2900",
"is_group": 0,
"root_type": "Liability"
}
}
},
"Equity": {
"account_number": "3000",
"is_group": 1,
"root_type": "Equity",
"STOCKHOLDER'S EQUITY": {
"account_number": "3001",
"is_group": 1,
"root_type": "Equity",
"Capital Stocks": {
"account_number": "3100",
"is_group": 1,
"root_type": "Equity",
"Capital Stocks": {
"account_number": "3101",
"is_group": 0,
"root_type": "Equity"
}
},
"Subscription Receivable": {
"account_number": "3200",
"is_group": 1,
"root_type": "Equity",
"Subscription Receivable": {
"account_number": "3201",
"is_group": 0,
"root_type": "Equity"
}
},
"Retained Earnings": {
"account_number": "3300",
"is_group": 1,
"root_type": "Equity",
"Retained Earnings": {
"account_number": "3301",
"is_group": 0,
"root_type": "Equity"
}
},
"Current Year (Profit/Loss)": {
"account_number": "3400",
"is_group": 0,
"root_type": "Equity"
},
"Drawings": {
"account_number": "3500",
"is_group": 1,
"root_type": "Equity",
"Drawings": {
"account_number": "3501",
"is_group": 0,
"root_type": "Equity"
}
}
}
},
"Income": {
"account_number": "4000",
"is_group": 1,
"root_type": "Income",
"Gross Sales": {
"account_number": "4100",
"is_group": 1,
"root_type": "Income",
"Sales": {
"account_number": "4101",
"is_group": 0,
"root_type": "Income"
}
},
"Sales Adjustment": {
"account_number": "4200",
"is_group": 1,
"root_type": "Income",
"Sales Return And Allowance": {
"account_number": "4201",
"is_group": 0,
"root_type": "Income"
}
},
"Sales Discount": {
"account_number": "4300",
"is_group": 1,
"root_type": "Income",
"Sales Discount": {
"account_number": "4301",
"is_group": 0,
"root_type": "Income"
}
},
"Other Income": {
"account_number": "6000",
"is_group": 1,
"root_type": "Income",
"Interest Income Bank": {
"account_number": "6010",
"is_group": 1,
"root_type": "Income",
"Interest Income Bank": {
"account_number": "6011",
"is_group": 0,
"root_type": "Income"
}
},
"Dividend Income": {
"account_number": "6020",
"is_group": 1,
"root_type": "Income",
"Dividend Income": {
"account_number": "6021",
"is_group": 0,
"root_type": "Income"
}
}
}
},
"Expense": {
"account_number": "5000",
"is_group": 1,
"root_type": "Expense",
"Operating Expenses": {
"account_number": "5100",
"is_group": 1,
"root_type": "Expense",
"Salaries, Wages": {
"account_number": "5101",
"is_group": 0,
"root_type": "Expense"
},
"13th Month Pay & Bonus": {
"account_number": "5102",
"is_group": 0,
"root_type": "Expense"
},
"Overtime & Night Diff": {
"account_number": "5103",
"is_group": 0,
"root_type": "Expense"
},
"Incentive/Performance Bonus": {
"account_number": "5104",
"is_group": 0,
"root_type": "Expense"
},
"Employees Benefits": {
"account_number": "5105",
"is_group": 0,
"root_type": "Expense"
},
"Advertising & Promotions": {
"account_number": "5106",
"is_group": 0,
"root_type": "Expense"
},
"Amortization of Leasehold Improvement": {
"account_number": "5107",
"is_group": 0,
"root_type": "Expense"
},
"Amortization of Pre-Operating": {
"account_number": "5108",
"is_group": 0,
"root_type": "Expense"
},
"Amortization of System Development": {
"account_number": "5109",
"is_group": 0,
"root_type": "Expense"
},
"Audit & Legal Fee": {
"account_number": "5110",
"is_group": 0,
"root_type": "Expense"
},
"Bad Debts Expenses": {
"account_number": "5111",
"is_group": 0,
"root_type": "Expense"
},
"Client Service & Maintenance": {
"account_number": "5112",
"is_group": 0,
"root_type": "Expense"
},
"Commission Expenses": {
"account_number": "5113",
"is_group": 0,
"root_type": "Expense"
},
"Communications": {
"account_number": "5114",
"is_group": 0,
"root_type": "Expense"
},
"Contractual Services": {
"account_number": "5115",
"is_group": 0,
"root_type": "Expense"
},
"Depreciation Expenses": {
"account_number": "5116",
"is_group": 0,
"root_type": "Expense",
"account_type": "Depreciation"
},
"Donation & Contribution": {
"account_number": "5117",
"is_group": 0,
"root_type": "Expense"
},
"Dues & Subscription": {
"account_number": "5118",
"is_group": 0,
"root_type": "Expense"
},
"Employee Med/Dental/Hosp Expenses": {
"account_number": "5119",
"is_group": 0,
"root_type": "Expense"
},
"Employee Uniforms": {
"account_number": "5120",
"is_group": 0,
"root_type": "Expense"
},
"Equipage": {
"account_number": "5121",
"is_group": 0,
"root_type": "Expense"
},
"Expenses for Reclassification": {
"account_number": "5122",
"is_group": 0,
"root_type": "Expense"
},
"Gas & Oil": {
"account_number": "5123",
"is_group": 0,
"root_type": "Expense"
},
"Insurance Expenses": {
"account_number": "5124",
"is_group": 0,
"root_type": "Expense"
},
"Light & Water": {
"account_number": "5125",
"is_group": 0,
"root_type": "Expense"
},
"Local/Overseas Travel": {
"account_number": "5126",
"is_group": 0,
"root_type": "Expense"
},
"Meals & Transportation Expenses": {
"account_number": "5127",
"is_group": 0,
"root_type": "Expense"
},
"Meeting & Conferences": {
"account_number": "5128",
"is_group": 0,
"root_type": "Expense"
},
"Miscellaneous Expenses": {
"account_number": "5129",
"is_group": 0,
"root_type": "Expense"
},
"Mockup Expenses": {
"account_number": "5130",
"is_group": 0,
"root_type": "Expense"
},
"Obsolescence Expenses": {
"account_number": "5131",
"is_group": 0,
"root_type": "Expense"
},
"Other Support Cost": {
"account_number": "5132",
"is_group": 0,
"root_type": "Expense"
},
"Pag-ibig Contribution": {
"account_number": "5133",
"is_group": 0,
"root_type": "Expense"
},
"Performance Bonds": {
"account_number": "5134",
"is_group": 0,
"root_type": "Expense"
},
"Pre Employment Expenses": {
"account_number": "5135",
"is_group": 0,
"root_type": "Expense"
},
"Professional Fees": {
"account_number": "5136",
"is_group": 0,
"root_type": "Expense"
},
"Recruitment & Employment": {
"account_number": "5137",
"is_group": 0,
"root_type": "Expense"
},
"Rent Expenses": {
"account_number": "5138",
"is_group": 0,
"root_type": "Expense"
},
"Rent Expenses Others": {
"account_number": "5139",
"is_group": 0,
"root_type": "Expense"
},
"Repairs & Maintenance": {
"account_number": "5140",
"is_group": 0,
"root_type": "Expense"
},
"Representation Expenses": {
"account_number": "5141",
"is_group": 0,
"root_type": "Expense"
},
"Research & Development": {
"account_number": "5142",
"is_group": 0,
"root_type": "Expense"
},
"Security Expenses": {
"account_number": "5143",
"is_group": 0,
"root_type": "Expense"
},
"Shared Services Fee": {
"account_number": "5144",
"is_group": 0,
"root_type": "Expense"
},
"SSS/Medicare/EC Contributions": {
"account_number": "5145",
"is_group": 0,
"root_type": "Expense"
},
"Stationery & Supplies": {
"account_number": "5146",
"is_group": 0,
"root_type": "Expense"
},
"Taxes & Licenses": {
"account_number": "5147",
"is_group": 0,
"root_type": "Expense",
"account_type": "Tax"
},
"Training & Seminar": {
"account_number": "5148",
"is_group": 0,
"root_type": "Expense"
}
},
"Stock Adjustment": {
"account_number": "5200",
"is_group": 0,
"root_type": "Expense",
"account_type": "Stock Adjustment"
},
"Round Off": {
"account_number": "5300",
"is_group": 0,
"root_type": "Expense",
"account_type": "Round Off"
},
"Expenses Included In Valuation": {
"account_number": "5400",
"is_group": 0,
"root_type": "Expense",
"account_type": "Expenses Included In Valuation"
}
}
}
}

View File

@@ -82,13 +82,15 @@ class AccountingDimension(Document):
else:
frappe.throw(_("Company {0} is added more than once").format(frappe.bold(default.company)))
def after_insert(self):
def on_update(self):
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)
else:
frappe.enqueue(
make_dimension_in_accounting_doctypes, doc=self, queue="long", enqueue_after_commit=True
)
frappe.flags.accounting_dimensions = None
frappe.flags.accounting_dimensions_details = None
def on_trash(self):
if frappe.flags.in_test:
@@ -103,10 +105,6 @@ class AccountingDimension(Document):
if not self.fieldname:
self.fieldname = scrub(self.label)
def on_update(self):
frappe.flags.accounting_dimensions = None
frappe.flags.accounting_dimensions_details = None
def make_dimension_in_accounting_doctypes(doc, doclist=None):
if not doclist:

View File

@@ -30,16 +30,6 @@ frappe.ui.form.on("Accounts Settings", {
add_taxes_from_item_tax_template(frm) {
toggle_tax_settings(frm, "add_taxes_from_item_tax_template");
},
drop_ar_procedures: function (frm) {
frm.call({
doc: frm.doc,
method: "drop_ar_sql_procedures",
callback: function (r) {
frappe.show_alert(__("Procedures dropped"), 5);
},
});
},
});
function toggle_tax_settings(frm, field_name) {

View File

@@ -95,7 +95,6 @@
"receivable_payable_fetch_method",
"default_ageing_range",
"column_break_ntmi",
"drop_ar_procedures",
"legacy_section",
"ignore_is_opening_check_for_reporting",
"payment_request_settings",
@@ -216,7 +215,7 @@
"description": "Payment Terms from orders will be fetched into the invoices as is",
"fieldname": "automatically_fetch_payment_terms",
"fieldtype": "Check",
"label": "Automatically Fetch Payment Terms from Order"
"label": "Automatically Fetch Payment Terms from Order/Quotation"
},
{
"description": "The percentage you are allowed to bill more against the amount ordered. For example, if the order value is $100 for an item and tolerance is set as 10%, then you are allowed to bill up to $110 ",
@@ -307,7 +306,7 @@
},
{
"default": "0",
"description": "Learn about <a href=\"https://docs.frappe.io/erpnext/user/manual/en/common_party_accounting\">Common Party</a>",
"description": "Learn about <a href=\"https://docs.erpnext.com/docs/v13/user/manual/en/accounts/articles/common_party_accounting#:~:text=Common%20Party%20Accounting%20in%20ERPNext,Invoice%20against%20a%20primary%20Supplier.\">Common Party</a>",
"fieldname": "enable_common_party_accounting",
"fieldtype": "Check",
"label": "Enable Common Party Accounting"
@@ -561,7 +560,7 @@
"fieldname": "receivable_payable_fetch_method",
"fieldtype": "Select",
"label": "Data Fetch Method",
"options": "Buffered Cursor\nUnBuffered Cursor\nRaw SQL"
"options": "Buffered Cursor\nUnBuffered Cursor"
},
{
"fieldname": "accounts_receivable_payable_tuning_section",
@@ -623,13 +622,6 @@
"fieldname": "column_break_ntmi",
"fieldtype": "Column Break"
},
{
"depends_on": "eval:doc.receivable_payable_fetch_method == \"Raw SQL\"",
"description": "Drops existing SQL Procedures and Function setup by Accounts Receivable report",
"fieldname": "drop_ar_procedures",
"fieldtype": "Button",
"label": "Drop Procedures"
},
{
"default": "0",
"fieldname": "fetch_valuation_rate_for_internal_transaction",
@@ -671,7 +663,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-12-26 19:46:55.093717",
"modified": "2026-05-18 12:16:33.679345",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -60,7 +60,7 @@ class AccountsSettings(Document):
merge_similar_account_heads: DF.Check
over_billing_allowance: DF.Currency
post_change_gl_entries: DF.Check
receivable_payable_fetch_method: DF.Literal["Buffered Cursor", "UnBuffered Cursor", "Raw SQL"]
receivable_payable_fetch_method: DF.Literal["Buffered Cursor", "UnBuffered Cursor"]
receivable_payable_remarks_length: DF.Int
reconciliation_queue_size: DF.Int
role_allowed_to_over_bill: DF.Link | None
@@ -170,11 +170,3 @@ class AccountsSettings(Document):
),
title=_("Auto Tax Settings Error"),
)
@frappe.whitelist()
def drop_ar_sql_procedures(self):
from erpnext.accounts.report.accounts_receivable.accounts_receivable import InitSQLProceduresForAR
frappe.db.sql(f"drop function if exists {InitSQLProceduresForAR.genkey_function_name}")
frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.init_procedure_name}")
frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.allocate_procedure_name}")

View File

@@ -131,6 +131,7 @@ def get_default_company_bank_account(company, party_type, party):
@frappe.whitelist()
def get_bank_account_details(bank_account):
frappe.has_permission("Bank Account", doc=bank_account, ptype="read", throw=True)
return frappe.get_cached_value(
"Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1
)

View File

@@ -5,7 +5,9 @@
import frappe
from frappe import _, msgprint
from frappe.model.document import Document
from frappe.query_builder import Case
from frappe.query_builder.custom import ConstantColumn
from frappe.query_builder.functions import Coalesce, Sum
from frappe.utils import flt, fmt_money, get_link_to_form, getdate
from pypika import Order
@@ -88,7 +90,14 @@ class BankClearance(Document):
@frappe.whitelist()
def update_clearance_date(self):
clearance_date_updated = False
payment_docs = []
for d in self.get("payment_entries"):
if d.payment_document not in payment_docs:
payment_docs.append(d.payment_document)
for doctype in payment_docs:
frappe.has_permission(doctype, "write", throw=True)
for d in self.get("payment_entries"):
if d.clearance_date:
if not d.payment_document:
@@ -136,65 +145,162 @@ def get_payment_entries_for_bank_clearance(
):
entries = []
condition = ""
pe_condition = ""
journal_entry = frappe.qb.DocType("Journal Entry")
journal_entry_account = frappe.qb.DocType("Journal Entry Account")
journal_entry_query = (
frappe.qb.from_(journal_entry_account)
.inner_join(journal_entry)
.on(journal_entry_account.parent == journal_entry.name)
.select(
ConstantColumn("Journal Entry").as_("payment_document"),
journal_entry.name.as_("payment_entry"),
journal_entry.cheque_no.as_("cheque_number"),
journal_entry.cheque_date,
Sum(journal_entry_account.debit_in_account_currency).as_("debit"),
Sum(journal_entry_account.credit_in_account_currency).as_("credit"),
journal_entry.posting_date,
journal_entry_account.against_account,
journal_entry.clearance_date,
journal_entry_account.account_currency,
)
.where(
(journal_entry_account.account == account)
& (journal_entry.docstatus == 1)
& (journal_entry.posting_date >= from_date)
& (journal_entry.posting_date <= to_date)
& (journal_entry.is_opening == "No")
)
)
if not include_reconciled_entries:
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
pe_condition = "and (pe.clearance_date IS NULL or pe.clearance_date='0000-00-00')"
journal_entry_query = journal_entry_query.where(
(journal_entry.clearance_date.isnull()) | (journal_entry.clearance_date == "0000-00-00")
)
journal_entries = frappe.db.sql(
f"""
select
"Journal Entry" as payment_document, t1.name as payment_entry,
t1.cheque_no as cheque_number, t1.cheque_date,
sum(t2.debit_in_account_currency) as debit, sum(t2.credit_in_account_currency) as credit,
t1.posting_date, t2.against_account, t1.clearance_date, t2.account_currency
from
`tabJournal Entry` t1, `tabJournal Entry Account` t2
where
t2.parent = t1.name and t2.account = %(account)s and t1.docstatus=1
and t1.posting_date >= %(from)s and t1.posting_date <= %(to)s
and ifnull(t1.is_opening, 'No') = 'No' {condition}
group by t2.account, t1.name
order by t1.posting_date ASC, t1.name DESC
""",
{"account": account, "from": from_date, "to": to_date},
as_dict=1,
journal_entries = (
journal_entry_query.groupby(journal_entry_account.account, journal_entry.name)
.orderby(journal_entry.posting_date)
.orderby(journal_entry.name, order=Order.desc)
).run(as_dict=True)
pe = frappe.qb.DocType("Payment Entry")
company = frappe.qb.DocType("Company")
payment_entry_query = (
frappe.qb.from_(pe)
.join(company)
.on(pe.company == company.name)
.select(
ConstantColumn("Payment Entry").as_("payment_document"),
pe.name.as_("payment_entry"),
pe.reference_no.as_("cheque_number"),
pe.reference_date.as_("cheque_date"),
(
Case()
.when(
pe.paid_from == account,
(
pe.paid_amount
+ (
Case()
.when(
(pe.payment_type == "Pay")
& (company.default_currency == pe.paid_from_account_currency),
pe.base_total_taxes_and_charges,
)
.else_(pe.total_taxes_and_charges)
)
),
)
.else_(0)
).as_("credit"),
(
Case()
.when(pe.paid_from == account, 0)
.else_(
pe.received_amount
+ (
Case()
.when(
company.default_currency == pe.paid_to_account_currency,
pe.base_total_taxes_and_charges,
)
.else_(pe.total_taxes_and_charges)
)
)
).as_("debit"),
pe.posting_date,
Coalesce(pe.party, Case().when(pe.paid_from == account, pe.paid_to).else_(pe.paid_from)).as_(
"against_account"
),
pe.clearance_date,
(
Case()
.when(pe.paid_to == account, pe.paid_to_account_currency)
.else_(pe.paid_from_account_currency)
).as_("account_currency"),
)
.where(
((pe.paid_from == account) | (pe.paid_to == account))
& (pe.docstatus == 1)
& (pe.posting_date >= from_date)
& (pe.posting_date <= to_date)
)
)
payment_entries = frappe.db.sql(
f"""
select
"Payment Entry" as payment_document, pe.name as payment_entry,
pe.reference_no as cheque_number, pe.reference_date as cheque_date,
if(pe.paid_from=%(account)s, pe.paid_amount + if(pe.payment_type = 'Pay' and c.default_currency = pe.paid_from_account_currency, pe.base_total_taxes_and_charges, pe.total_taxes_and_charges) , 0) as credit,
if(pe.paid_from=%(account)s, 0, pe.received_amount + pe.total_taxes_and_charges) as debit,
pe.posting_date, ifnull(pe.party,if(pe.paid_from=%(account)s,pe.paid_to,pe.paid_from)) as against_account, pe.clearance_date,
if(pe.paid_to=%(account)s, pe.paid_to_account_currency, pe.paid_from_account_currency) as account_currency
from `tabPayment Entry` as pe
join `tabCompany` c on c.name = pe.company
where
(pe.paid_from=%(account)s or pe.paid_to=%(account)s) and pe.docstatus=1
and pe.posting_date >= %(from)s and pe.posting_date <= %(to)s
{pe_condition}
order by
pe.posting_date ASC, pe.name DESC
""",
{
"account": account,
"from": from_date,
"to": to_date,
},
as_dict=1,
if not include_reconciled_entries:
payment_entry_query = payment_entry_query.where(
(pe.clearance_date.isnull()) | (pe.clearance_date == "0000-00-00")
)
payment_entries = (payment_entry_query.orderby(pe.posting_date).orderby(pe.name, order=Order.desc)).run(
as_dict=True
)
pos_sales_invoices, pos_purchase_invoices = [], []
acc = frappe.qb.DocType("Account")
pi = frappe.qb.DocType("Purchase Invoice")
paid_purchase_invoices_query = (
frappe.qb.from_(pi)
.inner_join(acc)
.on(pi.cash_bank_account == acc.name)
.select(
ConstantColumn("Purchase Invoice").as_("payment_document"),
pi.name.as_("payment_entry"),
pi.paid_amount.as_("credit"),
pi.posting_date,
pi.supplier.as_("against_account"),
pi.bill_no.as_("cheque_number"),
pi.clearance_date,
acc.account_currency,
ConstantColumn(0).as_("debit"),
)
.where(
(pi.docstatus == 1)
& (pi.is_paid == 1)
& (pi.cash_bank_account == account)
& (pi.posting_date >= from_date)
& (pi.posting_date <= to_date)
)
)
if not include_reconciled_entries:
paid_purchase_invoices_query = paid_purchase_invoices_query.where(
(pi.clearance_date.isnull()) | (pi.clearance_date == "0000-00-00")
)
paid_purchase_invoices = (
paid_purchase_invoices_query.orderby(pi.posting_date).orderby(pi.name, order=Order.desc)
).run(as_dict=True)
pos_sales_invoices = []
if include_pos_transactions:
si_payment = frappe.qb.DocType("Sales Invoice Payment")
si = frappe.qb.DocType("Sales Invoice")
acc = frappe.qb.DocType("Account")
pos_sales_invoices = (
pos_sales_invoices_query = (
frappe.qb.from_(si_payment)
.inner_join(si)
.on(si_payment.parent == si.name)
@@ -217,38 +323,22 @@ def get_payment_entries_for_bank_clearance(
& (si.posting_date >= from_date)
& (si.posting_date <= to_date)
)
.orderby(si.posting_date)
.orderby(si.name, order=Order.desc)
).run(as_dict=True)
)
pi = frappe.qb.DocType("Purchase Invoice")
if not include_reconciled_entries:
pos_sales_invoices_query = pos_sales_invoices_query.where(
(si_payment.clearance_date.isnull()) | (si_payment.clearance_date == "0000-00-00")
)
pos_purchase_invoices = (
frappe.qb.from_(pi)
.inner_join(acc)
.on(pi.cash_bank_account == acc.name)
.select(
ConstantColumn("Purchase Invoice").as_("payment_document"),
pi.name.as_("payment_entry"),
pi.paid_amount.as_("credit"),
pi.posting_date,
pi.supplier.as_("against_account"),
pi.clearance_date,
acc.account_currency,
ConstantColumn(0).as_("debit"),
)
.where(
(pi.docstatus == 1)
& (pi.cash_bank_account == account)
& (pi.posting_date >= from_date)
& (pi.posting_date <= to_date)
)
.orderby(pi.posting_date)
.orderby(pi.name, order=Order.desc)
pos_sales_invoices = (
pos_sales_invoices_query.orderby(si.posting_date).orderby(si.name, order=Order.desc)
).run(as_dict=True)
entries = (
list(payment_entries) + list(journal_entries) + list(pos_sales_invoices) + list(pos_purchase_invoices)
list(payment_entries)
+ list(journal_entries)
+ list(pos_sales_invoices)
+ list(paid_purchase_invoices)
)
return entries

View File

@@ -2,6 +2,15 @@
// For license information, please see license.txt
frappe.ui.form.on("Bank Statement Import", {
onload(frm) {
frm.set_query("bank_account", function (doc) {
return {
filters: {
company: doc.company,
},
};
});
},
setup(frm) {
frappe.realtime.on("data_import_refresh", ({ data_import }) => {
frm.import_in_progress = false;

View File

@@ -1,7 +1,6 @@
{
"actions": [],
"autoname": "format:Bank Statement Import on {creation}",
"beta": 1,
"creation": "2019-08-04 14:16:08.318714",
"doctype": "DocType",
"editable_grid": 1,
@@ -211,10 +210,11 @@
],
"hide_toolbar": 1,
"links": [],
"modified": "2024-06-25 17:32:07.658250",
"modified": "2026-05-30 20:51:10.353723",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Import",
"naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
@@ -230,7 +230,9 @@
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -398,7 +398,7 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
frappe.get_doc(
{
"doctype": "Customer",
"customer_group": "All Customer Groups",
"customer_group": "Individual",
"customer_type": "Company",
"customer_name": "Poore Simon's",
}
@@ -429,7 +429,7 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
frappe.get_doc(
{
"doctype": "Customer",
"customer_group": "All Customer Groups",
"customer_group": "Individual",
"customer_type": "Company",
"customer_name": "Fayva",
}

View File

@@ -121,7 +121,7 @@ class BisectAccountingStatements(Document):
cur_node.save()
@frappe.whitelist()
@frappe.whitelist(methods=["POST"])
def build_tree(self):
frappe.db.delete("Bisect Nodes")

View File

@@ -6,12 +6,14 @@ frappe.provide("erpnext.cheque_print");
frappe.ui.form.on("Cheque Print Template", {
refresh: function (frm) {
if (!frm.doc.__islocal) {
frm.add_custom_button(
frm.doc.has_print_format ? __("Update Print Format") : __("Create Print Format"),
function () {
erpnext.cheque_print.view_cheque_print(frm);
}
).addClass("btn-primary");
if (frappe.user.has_role("System Manager")) {
frm.add_custom_button(
frm.doc.has_print_format ? __("Update Print Format") : __("Create Print Format"),
function () {
erpnext.cheque_print.view_cheque_print(frm);
}
).addClass("btn-primary");
}
$(frm.fields_dict.cheque_print_preview.wrapper).empty();

View File

@@ -48,6 +48,8 @@ class ChequePrintTemplate(Document):
@frappe.whitelist()
def create_or_update_cheque_print_format(template_name):
frappe.only_for("System Manager")
if not frappe.db.exists("Print Format", template_name):
cheque_print = frappe.new_doc("Print Format")
cheque_print.update(

View File

@@ -11,22 +11,28 @@ frappe.ui.form.on("Currency Exchange Settings", {
},
callback: function (r) {
if (r && r.message) {
let result = [],
params = {};
if (frm.doc.service_provider == "exchangerate.host") {
let result = ["result"];
let params = {
result = ["result"];
params = {
date: "{transaction_date}",
from: "{from_currency}",
to: "{to_currency}",
};
add_param(frm, r.message, params, result);
} else if (["frankfurter.app", "frankfurter.dev"].includes(frm.doc.service_provider)) {
let result = ["rates", "{to_currency}"];
let params = {
result = ["rates", "{to_currency}"];
params = {
base: "{from_currency}",
symbols: "{to_currency}",
};
add_param(frm, r.message, params, result);
} else if (frm.doc.service_provider == "frankfurter.dev - v2") {
result = ["rate"];
params = {
date: "{transaction_date}",
};
}
add_param(frm, r.message, params, result);
}
},
});

View File

@@ -78,7 +78,7 @@
"fieldname": "service_provider",
"fieldtype": "Select",
"label": "Service Provider",
"options": "frankfurter.dev\nexchangerate.host\nCustom",
"options": "frankfurter.dev\nexchangerate.host\nfrankfurter.dev - v2\nCustom",
"reqd": 1
},
{
@@ -104,7 +104,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-11-25 13:03:41.896424",
"modified": "2026-06-15 11:25:55.873110",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Currency Exchange Settings",
@@ -121,24 +121,11 @@
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"role": "Accounts User",
"share": 1,
"write": 1
"share": 1
}
],
"row_format": "Dynamic",

View File

@@ -29,7 +29,7 @@ class CurrencyExchangeSettings(Document):
disabled: DF.Check
req_params: DF.Table[CurrencyExchangeSettingsDetails]
result_key: DF.Table[CurrencyExchangeSettingsResult]
service_provider: DF.Literal["frankfurter.dev", "exchangerate.host", "Custom"]
service_provider: DF.Literal["frankfurter.dev", "exchangerate.host", "frankfurter.dev - v2", "Custom"]
url: DF.Data | None
use_http: DF.Check
# end: auto-generated types
@@ -70,6 +70,14 @@ class CurrencyExchangeSettings(Document):
self.append("req_params", {"key": "base", "value": "{from_currency}"})
self.append("req_params", {"key": "symbols", "value": "{to_currency}"})
elif self.service_provider == "frankfurter.dev - v2":
self.set("result_key", [])
self.set("req_params", [])
self.api_endpoint = get_api_endpoint(self.service_provider, self.use_http)
self.append("result_key", {"key": "rate"})
self.append("req_params", {"key": "date", "value": "{transaction_date}"})
def validate_parameters(self):
params = {}
for row in self.req_params:
@@ -105,13 +113,20 @@ class CurrencyExchangeSettings(Document):
@frappe.whitelist()
def get_api_endpoint(service_provider: str | None = None, use_http: bool = False):
if service_provider and service_provider in ["exchangerate.host", "frankfurter.dev", "frankfurter.app"]:
if service_provider and service_provider in [
"exchangerate.host",
"frankfurter.dev",
"frankfurter.app",
"frankfurter.dev - v2",
]:
if service_provider == "exchangerate.host":
api = "api.exchangerate.host/convert"
elif service_provider == "frankfurter.app":
api = "api.frankfurter.app/{transaction_date}"
elif service_provider == "frankfurter.dev":
api = "api.frankfurter.dev/v1/{transaction_date}"
elif service_provider == "frankfurter.dev - v2":
api = "api.frankfurter.dev/v2/rate/{from_currency}/{to_currency}"
protocol = "https://"
if use_http:

View File

@@ -2,7 +2,6 @@
"actions": [],
"allow_events_in_timeline": 1,
"autoname": "naming_series:",
"beta": 1,
"creation": "2019-07-05 16:34:31.013238",
"doctype": "DocType",
"engine": "InnoDB",
@@ -400,7 +399,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2024-11-26 13:46:07.760867",
"modified": "2026-05-30 20:40:30.851842",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Dunning",
@@ -449,9 +448,10 @@
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "ASC",
"states": [],
"title_field": "customer_name",
"track_changes": 1
}
}

View File

@@ -1,7 +1,6 @@
{
"actions": [],
"allow_rename": 1,
"beta": 1,
"creation": "2019-12-04 04:59:08.003664",
"doctype": "DocType",
"editable_grid": 1,
@@ -107,7 +106,7 @@
"link_fieldname": "dunning_type"
}
],
"modified": "2021-11-13 00:25:35.659283",
"modified": "2026-05-30 20:40:09.952533",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Dunning Type",
@@ -151,7 +150,9 @@
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -1,108 +1,41 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-11-22 23:47:02.804568",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"creation": "2018-11-22 23:47:02.804568",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"tax_type",
"tax_rate"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "tax_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Tax",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "tax_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Tax",
"options": "Account",
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "tax_rate",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Tax Rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldname": "tax_rate",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Tax Rate"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-12-21 23:51:39.445198",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Item Tax Template Detail",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
],
"istable": 1,
"links": [],
"modified": "2026-04-30 23:49:27.020639",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Item Tax Template Detail",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -378,15 +378,17 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
accounts_add(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
row.exchange_rate = 1;
$.each(doc.accounts, function (i, d) {
if (d.account && d.party && d.party_type) {
row.account = d.account;
row.party = d.party;
row.party_type = d.party_type;
row.exchange_rate = d.exchange_rate;
}
});
if (!row.exchange_rate) row.exchange_rate = 1;
if (!row.account) {
$.each(doc.accounts, function (i, d) {
if (d.account && d.party && d.party_type) {
row.account = d.account;
row.party = d.party;
row.party_type = d.party_type;
row.exchange_rate = d.exchange_rate;
}
});
}
// set difference
if (doc.difference) {

View File

@@ -214,6 +214,8 @@ class JournalEntry(AccountsController):
def on_cancel(self):
# References for this Journal are removed on the `on_cancel` event in accounts_controller
super().on_cancel()
from_doc_events = getattr(self, "ignore_linked_doctypes", ())
self.ignore_linked_doctypes = (
"GL Entry",
"Stock Ledger Entry",
@@ -226,6 +228,10 @@ class JournalEntry(AccountsController):
"Unreconcile Payment Entries",
"Advance Payment Ledger Entry",
)
if from_doc_events and from_doc_events != self.ignore_linked_doctypes:
self.ignore_linked_doctypes = self.ignore_linked_doctypes + from_doc_events
self.make_gl_entries(1)
self.unlink_advance_entry_reference()
self.unlink_asset_reference()
@@ -263,6 +269,9 @@ class JournalEntry(AccountsController):
frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation"))
def validate_stock_accounts(self):
if not erpnext.is_perpetual_inventory_enabled(self.company):
return
stock_accounts = get_stock_accounts(self.company, accounts=self.accounts)
for account in stock_accounts:
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(

View File

@@ -1,6 +1,6 @@
{
"actions": [],
"allow_copy": 1,
"beta": 1,
"creation": "2017-08-29 02:22:54.947711",
"doctype": "DocType",
"editable_grid": 1,
@@ -64,10 +64,10 @@
"options": "Cost Center"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"collapsible": 1,
@@ -82,7 +82,8 @@
],
"hide_toolbar": 1,
"issingle": 1,
"modified": "2022-01-04 15:25:06.053187",
"links": [],
"modified": "2026-05-30 20:43:36.282738",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Opening Invoice Creation Tool",
@@ -99,7 +100,9 @@
}
],
"quick_entry": 1,
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -5,7 +5,7 @@
import frappe
from frappe import _, scrub
from frappe.model.document import Document
from frappe.utils import flt, nowdate
from frappe.utils import escape_html, flt, nowdate
from frappe.utils.background_jobs import enqueue, is_job_enqueued
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -31,6 +31,7 @@ class OpeningInvoiceCreationTool(Document):
create_missing_party: DF.Check
invoice_type: DF.Literal["Sales", "Purchase"]
invoices: DF.Table[OpeningInvoiceCreationToolItem]
project: DF.Link | None
# end: auto-generated types
def onload(self):
@@ -84,6 +85,11 @@ class OpeningInvoiceCreationTool(Document):
)
prepare_invoice_summary(doctype, invoices)
invoices_summary_companies = list(invoices_summary.keys())
for company in invoices_summary_companies:
invoices_summary[escape_html(company)] = invoices_summary.pop(company)
return invoices_summary, max_count
def validate_company(self):

View File

@@ -209,7 +209,7 @@ def make_customer(customer=None):
{
"doctype": "Customer",
"customer_name": customer_name,
"customer_group": "All Customer Groups",
"customer_group": "Individual",
"customer_type": "Company",
"territory": "All Territories",
}

View File

@@ -725,31 +725,12 @@ frappe.ui.form.on("Payment Entry", {
if (!frm.doc.paid_from_account_currency || !frm.doc.company) return;
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if (frm.doc.paid_from_account_currency == company_currency) {
frm.set_value("source_exchange_rate", 1);
} else if (frm.doc.paid_from) {
if (["Internal Transfer", "Pay"].includes(frm.doc.payment_type)) {
let company_currency = frappe.get_doc(":Company", frm.doc.company)?.default_currency;
frappe.call({
method: "erpnext.setup.utils.get_exchange_rate",
args: {
from_currency: frm.doc.paid_from_account_currency,
to_currency: company_currency,
transaction_date: frm.doc.posting_date,
},
callback: function (r, rt) {
frm.set_value("source_exchange_rate", r.message);
},
});
} else {
frm.events.set_current_exchange_rate(
frm,
"source_exchange_rate",
frm.doc.paid_from_account_currency,
company_currency
);
}
}
frm.events.set_current_exchange_rate(
frm,
"source_exchange_rate",
frm.doc.paid_from_account_currency,
company_currency
);
},
paid_to_account_currency: function (frm) {
@@ -781,49 +762,24 @@ frappe.ui.form.on("Payment Entry", {
posting_date: function (frm) {
frm.events.paid_from_account_currency(frm);
frm.events.paid_to_account_currency(frm);
},
source_exchange_rate: function (frm) {
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if (frm.doc.paid_amount) {
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
// target exchange rate should always be same as source if both account currencies is same
if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
} else if (company_currency == frm.doc.paid_to_account_currency) {
frm.set_value("received_amount", frm.doc.base_paid_amount);
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
}
// set_unallocated_amount is called by below method,
// no need trigger separately
frm.events.set_total_allocated_amount(frm);
}
// Make read only if Accounts Settings doesn't allow stale rates
frm.set_df_property("source_exchange_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
},
target_exchange_rate: function (frm) {
frm.set_paid_amount_based_on_received_amount = true;
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if (frm.doc.received_amount) {
frm.set_value(
"base_received_amount",
flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate)
);
if (frm.doc.base_received_amount && frm.doc.source_exchange_rate) {
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
if (
!frm.doc.source_exchange_rate &&
frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency
) {
frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate);
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
} else if (company_currency == frm.doc.paid_from_account_currency) {
frm.set_value("paid_amount", frm.doc.base_received_amount);
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
// target exchange rate should always be same as source if both account currencies is same
if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
} else {
frm.set_value(
"paid_amount",
flt(frm.doc.base_paid_amount) / flt(frm.doc.source_exchange_rate)
);
}
// set_unallocated_amount is called by below method,
@@ -832,6 +788,32 @@ frappe.ui.form.on("Payment Entry", {
}
frm.set_paid_amount_based_on_received_amount = false;
// Make read only if Accounts Settings doesn't allow stale rates
frm.set_df_property("source_exchange_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
},
target_exchange_rate: function (frm) {
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if (frm.doc.base_paid_amount && frm.doc.target_exchange_rate) {
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
if (
!frm.doc.source_exchange_rate &&
frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency
) {
frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate);
} else {
frm.set_value(
"received_amount",
flt(frm.doc.base_received_amount) / flt(frm.doc.target_exchange_rate)
);
}
// set_unallocated_amount is called by below method,
// no need trigger separately
frm.events.set_total_allocated_amount(frm);
}
// Make read only if Accounts Settings doesn't allow stale rates
frm.set_df_property("target_exchange_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
},
@@ -840,11 +822,14 @@ frappe.ui.form.on("Payment Entry", {
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if (!frm.doc.received_amount) {
if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
frm.set_value("received_amount", frm.doc.paid_amount);
} else if (company_currency == frm.doc.paid_to_account_currency) {
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
if (company_currency == frm.doc.paid_to_account_currency) {
frm.set_value("received_amount", frm.doc.base_paid_amount);
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
} else if (frm.doc.target_exchange_rate) {
frm.set_value(
"received_amount",
flt(frm.doc.base_paid_amount) / flt(frm.doc.target_exchange_rate)
);
}
}
frm.trigger("reset_received_amount");
@@ -861,15 +846,14 @@ frappe.ui.form.on("Payment Entry", {
);
if (!frm.doc.paid_amount) {
if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
frm.set_value("paid_amount", frm.doc.received_amount);
if (frm.doc.target_exchange_rate) {
frm.set_value("source_exchange_rate", frm.doc.target_exchange_rate);
}
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
} else if (company_currency == frm.doc.paid_from_account_currency) {
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
if (company_currency == frm.doc.paid_from_account_currency) {
frm.set_value("paid_amount", frm.doc.base_received_amount);
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
} else if (frm.doc.source_exchange_rate) {
frm.set_value(
"paid_amount",
flt(frm.doc.base_received_amount) / flt(frm.doc.source_exchange_rate)
);
}
}
@@ -1789,6 +1773,35 @@ frappe.ui.form.on("Payment Entry", {
},
});
},
before_cancel: function (frm) {
return new Promise((resolve, reject) => {
frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_linked_bank_transactions",
args: { payment_entry: frm.doc.name },
callback: function (r) {
const linked = r.message || [];
if (!linked.length) {
resolve();
return;
}
const bt_links = linked
.map((name) => frappe.utils.get_form_link("Bank Transaction", name, true))
.join(", ");
frappe.confirm(
__(
"This Payment Entry is reconciled with {0}. Cancelling will automatically unreconcile it. Do you want to proceed?",
[bt_links]
),
() => resolve(),
() => reject(),
__("Yes"),
__("No")
);
},
});
});
},
});
frappe.ui.form.on("Payment Entry Reference", {

View File

@@ -350,7 +350,7 @@
"reqd": 1
},
{
"depends_on": "doc.received_amount",
"depends_on": "eval:doc.received_amount;",
"fieldname": "base_received_amount",
"fieldtype": "Currency",
"label": "Received Amount (Company Currency)",
@@ -800,7 +800,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2025-05-15 18:01:04.013025",
"modified": "2026-05-15 13:31:01.166010",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -2279,6 +2279,9 @@ def get_outstanding_reference_documents(args, validate=False):
if args.get("party_type") == "Member":
return
if args.get("party_type") and args.get("party"):
frappe.has_permission(args["party_type"], "read", args["party"], throw=True)
if not args.get("get_outstanding_invoices") and not args.get("get_orders_to_be_billed"):
args["get_outstanding_invoices"] = True
@@ -2306,22 +2309,20 @@ def get_outstanding_reference_documents(args, validate=False):
# Get positive outstanding sales /purchase invoices
condition = ""
if args.get("voucher_type") and args.get("voucher_no"):
condition = " and voucher_type={} and voucher_no={}".format(
frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])
)
condition = f" and voucher_type={frappe.db.escape(args['voucher_type'])} and voucher_no={frappe.db.escape(args['voucher_no'])}"
common_filter.append(ple.voucher_type == args["voucher_type"])
common_filter.append(ple.voucher_no == args["voucher_no"])
# Add cost center condition
if args.get("cost_center"):
condition += " and cost_center='%s'" % args.get("cost_center")
condition += f" and cost_center={frappe.db.escape(args.get('cost_center'))}"
accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center"))
# dynamic dimension filters
active_dimensions = get_dimensions()[0]
for dim in active_dimensions:
if args.get(dim.fieldname):
condition += f" and {dim.fieldname}='{args.get(dim.fieldname)}'"
condition += f" and {dim.fieldname}={frappe.db.escape(args.get(dim.fieldname))}"
accounting_dimensions_filter.append(ple[dim.fieldname] == args.get(dim.fieldname))
date_fields_dict = {
@@ -2330,18 +2331,19 @@ def get_outstanding_reference_documents(args, validate=False):
}
for fieldname, date_fields in date_fields_dict.items():
from_date = frappe.db.escape(str(args.get(date_fields[0]))) if args.get(date_fields[0]) else None
to_date = frappe.db.escape(str(args.get(date_fields[1]))) if args.get(date_fields[1]) else None
if args.get(date_fields[0]) and args.get(date_fields[1]):
condition += " and {} between '{}' and '{}'".format(
fieldname, args.get(date_fields[0]), args.get(date_fields[1])
)
condition += f" and {fieldname} between {from_date} and {to_date}"
posting_and_due_date.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
elif args.get(date_fields[0]):
# if only from date is supplied
condition += f" and {fieldname} >= '{args.get(date_fields[0])}'"
condition += f" and {fieldname} >= {from_date}"
posting_and_due_date.append(ple[fieldname].gte(args.get(date_fields[0])))
elif args.get(date_fields[1]):
# if only to date is supplied
condition += f" and {fieldname} <= '{args.get(date_fields[1])}'"
condition += f" and {fieldname} <= {to_date}"
posting_and_due_date.append(ple[fieldname].lte(args.get(date_fields[1])))
if args.get("company"):
@@ -2556,17 +2558,12 @@ def get_orders_to_be_billed(
if not voucher_type:
return []
# Add cost center condition
doc = frappe.get_doc({"doctype": voucher_type})
condition = ""
if doc and hasattr(doc, "cost_center") and doc.cost_center:
condition = " and cost_center='%s'" % cost_center
# dynamic dimension filters
active_dimensions = get_dimensions()[0]
condition = ""
active_dimensions = get_dimensions(True)[0]
for dim in active_dimensions:
if filters.get(dim.fieldname):
condition += f" and {dim.fieldname}='{filters.get(dim.fieldname)}'"
condition += f" and {dim.fieldname}={frappe.db.escape(filters.get(dim.fieldname))}"
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
@@ -2794,7 +2791,8 @@ def get_reference_details(
):
total_amount = outstanding_amount = exchange_rate = account = None
ref_doc = frappe.get_doc(reference_doctype, reference_name)
frappe.has_permission(reference_doctype, "read", reference_name, throw=True)
ref_doc = frappe.get_lazy_doc(reference_doctype, reference_name)
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company)
# Only applies for Reverse Payment Entries
@@ -3040,7 +3038,7 @@ def get_payment_entry(
pe, doc, discount_amount, base_total_discount_loss, party_account_currency
)
pe.set_exchange_rate(ref_doc=doc)
pe.set_exchange_rate()
pe.set_amounts()
# If PE is created from PR directly, then no need to find open PRs for the references
@@ -3605,3 +3603,16 @@ def make_payment_order(source_name, target_doc=None):
@erpnext.allow_regional
def add_regional_gl_entries(gl_entries, doc):
return
@frappe.whitelist()
def get_linked_bank_transactions(payment_entry: str) -> list:
frappe.has_permission("Payment Entry", ptype="read", doc=payment_entry, throw=True)
return frappe.get_all(
"Bank Transaction Payments",
filters={
"payment_document": "Payment Entry",
"payment_entry": payment_entry,
},
pluck="parent",
)

View File

@@ -200,6 +200,30 @@ class TestPaymentEntry(FrappeTestCase):
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 100)
def test_reference_outstanding_amount_on_advance_pull(self):
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
so = make_sales_order(qty=1, rate=1000)
pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Cash - _TC")
pe.paid_amount = pe.received_amount = 500
pe.references[0].allocated_amount = 500
pe.insert()
pe.submit()
so.reload()
self.assertEqual(so.advance_paid, 500)
si = make_sales_invoice(so.name)
si.allocate_advances_automatically = 1
si.save()
self.assertEqual(si.get("advances")[0].allocated_amount, 500)
self.assertEqual(si.get("advances")[0].reference_name, pe.name)
si.submit()
pe.load_from_db()
self.assertEqual(pe.references[0].reference_name, si.name)
self.assertEqual(pe.references[0].outstanding_amount, si.outstanding_amount)
def test_payment_entry_against_pi(self):
pi = make_purchase_invoice(
supplier="_Test Supplier USD",
@@ -513,6 +537,8 @@ class TestPaymentEntry(FrappeTestCase):
si.submit()
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC", bank_amount=4700)
pe.source_exchange_rate = 50
pe.set_amounts()
pe.reference_no = si.name
pe.reference_date = nowdate()
@@ -588,6 +614,8 @@ class TestPaymentEntry(FrappeTestCase):
pe = get_payment_entry(
"Sales Invoice", si.name, party_amount=20, bank_account="_Test Bank - _TC", bank_amount=900
)
pe.source_exchange_rate = 50
pe.set_amounts()
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
@@ -1937,6 +1965,37 @@ class TestPaymentEntry(FrappeTestCase):
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, pe.doctype, pe.name)
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, "Journal Entry", jv[0])
def test_project_name_in_exchange_gain_loss_entry(self):
si = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=50,
do_not_submit=True,
)
from erpnext.projects.doctype.project.test_project import make_project
si.project = make_project({"project_name": "_Test Project for Exchange Gain Loss Entry"}).name
si.submit()
pe = get_payment_entry("Sales Invoice", si.name)
pe.source_exchange_rate = 100
pe.insert()
pe.submit()
rows = frappe.get_all(
"Journal Entry Account",
or_filters=[{"reference_name": pe.name}, {"reference_name": si.name}],
fields=["project"],
)
self.assertEqual(len(rows), 2)
self.assertEqual(rows[0].project, si.project)
self.assertEqual(rows[1].project, si.project)
def create_payment_entry(**args):
payment_entry = frappe.new_doc("Payment Entry")
@@ -2043,6 +2102,7 @@ def create_customer(name="_Test Customer 2 USD", currency="USD"):
customer.customer_name = name
customer.default_currency = currency
customer.type = "Individual"
customer.customer_group = "Individual"
customer.save()
customer = customer.name
return customer

View File

@@ -22,6 +22,7 @@
"reqd": 1
},
{
"allow_on_submit": 1,
"fieldname": "cost_center",
"fieldtype": "Link",
"in_list_view": 1,
@@ -59,7 +60,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-11-05 16:07:47.307971",
"modified": "2026-03-11 14:26:11.312950",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Deduction",

View File

@@ -3,8 +3,9 @@
import frappe
from frappe import qb
from frappe.query_builder.functions import Count, Sum
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import nowdate
from frappe.utils import add_days, nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
@@ -80,6 +81,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
customer = frappe.new_doc("Customer")
customer.customer_name = name
customer.type = "Individual"
customer.customer_group = "Individual"
customer.save()
self.customer = customer.name
@@ -93,6 +95,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
posting_date = nowdate()
sinv = create_sales_invoice(
posting_date=posting_date,
qty=qty,
rate=rate,
company=self.company,
@@ -534,3 +537,82 @@ class TestPaymentLedgerEntry(FrappeTestCase):
# with references removed, deletion should be possible
so.delete()
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, so.doctype, so.name)
@change_settings(
"Accounts Settings",
{"enable_immutable_ledger": 1},
)
def test_reverse_entries_on_cancel_for_immutable_ledger(self):
invoice_posting_date = add_days(nowdate(), -5)
gle = qb.DocType("GL Entry")
ple = qb.DocType("Payment Ledger Entry")
si = self.create_sales_invoice(qty=1, rate=100, posting_date=invoice_posting_date)
gles_before = (
qb.from_(gle)
.select(
Count(gle.name),
)
.where((gle.voucher_type == si.doctype) & (gle.voucher_no == si.name) & (gle.is_cancelled == 0))
.run()[0][0]
)
ples_before = (
qb.from_(ple)
.select(
Count(ple.name),
)
.where((ple.voucher_type == si.doctype) & (ple.voucher_no == si.name) & (ple.delinked.eq(0)))
.run()[0][0]
)
si.cancel()
gles_after = (
qb.from_(gle)
.select(Count(gle.account))
.where((gle.voucher_type == si.doctype) & (gle.voucher_no == si.name) & (gle.is_cancelled == 0))
.run()[0][0]
)
self.assertEqual(gles_after, gles_before * 2)
ples_after = (
qb.from_(ple)
.select(
Count(ple.name),
)
.where((ple.voucher_type == si.doctype) & (ple.voucher_no == si.name) & (ple.delinked.eq(0)))
.run()[0][0]
)
self.assertEqual(ples_after, ples_before * 2)
# assert debit/credit are reversed
gl_entries = (
qb.from_(gle)
.select(gle.account, Sum(gle.debit).as_("total_debit"), Sum(gle.credit).as_("total_credit"))
.where((gle.voucher_type == si.doctype) & (gle.voucher_no == si.name) & (gle.is_cancelled == 0))
.groupby(gle.account)
.run(as_dict=True)
)
for gl in gl_entries:
with self.subTest(gl=gl):
self.assertEqual(gl.total_debit, gl.total_credit)
# assert amounts are reversed
pl_entries = (
qb.from_(ple)
.select(ple.account, Sum(ple.amount).as_("total_amount"))
.where((ple.voucher_type == si.doctype) & (ple.voucher_no == si.name) & (ple.delinked == 0))
.groupby(ple.account)
.run(as_dict=True)
)
for pl in pl_entries:
with self.subTest(pl=pl):
self.assertEqual(pl.total_amount, 0)
self.assertFalse(
frappe.db.exists(
"Payment Ledger Entry",
{"voucher_type": si.doctype, "voucher_no": si.name, "delinked": 1},
)
)

View File

@@ -2546,6 +2546,7 @@ def make_customer(customer_name, currency=None):
customer = frappe.new_doc("Customer")
customer.customer_name = customer_name
customer.type = "Individual"
customer.customer_group = "Individual"
if currency:
customer.default_currency = currency

View File

@@ -11,11 +11,12 @@ from erpnext import get_company_currency
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_account
from erpnext.accounts.doctype.payment_entry.payment_entry import (
get_payment_entry,
)
from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
from erpnext.accounts.party import get_party_account, get_party_bank_account
from erpnext.accounts.party import get_party_account
from erpnext.accounts.utils import get_account_currency, get_currency_precision
from erpnext.utilities import payment_app_import_guard

View File

@@ -195,7 +195,12 @@ class TestPaymentRequest(FrappeTestCase):
return_doc=1,
)
pe = pr.set_as_paid()
pe = pr.create_payment_entry(submit=False)
pe.source_exchange_rate = 50
pe.target_exchange_rate = 50
pe.set_amounts()
pe.insert(ignore_permissions=True)
pe.submit()
expected_gle = dict(
(d[0], d)
@@ -281,7 +286,12 @@ class TestPaymentRequest(FrappeTestCase):
pr = make_payment_request(dt=po_doc.doctype, dn=po_doc.name, recipient_id="nabin@erpnext.com")
pr = frappe.get_doc(pr).save().submit()
pe = pr.create_payment_entry()
pe = pr.create_payment_entry(submit=False)
pe.target_exchange_rate = 80
pe.paid_amount = 800
pe.set_amounts()
pe.insert(ignore_permissions=True)
pe.submit()
self.assertEqual(pe.base_paid_amount, 800)
self.assertEqual(pe.paid_amount, 800)
self.assertEqual(pe.base_received_amount, 800)

View File

@@ -46,8 +46,8 @@ frappe.ui.form.on("Period Closing Voucher", {
function () {
frappe.route_options = {
voucher_no: frm.doc.name,
from_date: frm.doc.posting_date,
to_date: moment(frm.doc.modified).format("YYYY-MM-DD"),
from_date: frm.doc.period_start_date,
to_date: frm.doc.period_end_date,
company: frm.doc.company,
categorize_by: "",
show_cancelled_entries: frm.doc.docstatus === 2,

View File

@@ -5,11 +5,13 @@
import unittest
import frappe
from frappe.tests.utils import change_settings
from frappe.utils import today
from erpnext.accounts.doctype.finance_book.test_finance_book import create_finance_book
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.general_ledger import make_reverse_gl_entries
from erpnext.accounts.utils import get_fiscal_year
@@ -351,6 +353,51 @@ class TestPeriodClosingVoucher(unittest.TestCase):
return pcv
@change_settings(
"Accounts Settings",
{"enable_immutable_ledger": 1},
)
def test_immutable_ledger_reverse_entry_uses_passed_posting_date_after_pcv(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
company = create_company()
cost_center = create_cost_center("Test Cost Center 1")
jv = make_journal_entry(
posting_date="2021-03-15",
amount=400,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center,
company=company,
save=False,
)
jv.company = company
jv.save()
jv.submit()
self.make_period_closing_voucher(posting_date="2021-03-31")
# Passed posting_date is after PCV end date, so cancellation should not fail.
make_reverse_gl_entries(
voucher_type="Journal Entry",
voucher_no=jv.name,
posting_date="2022-01-01",
)
totals_after_cancel = frappe.db.sql(
"""
select sum(debit) as total_debit, sum(credit) as total_credit
from `tabGL Entry`
where voucher_type=%s and voucher_no=%s and is_cancelled=0
""",
("Journal Entry", jv.name),
as_dict=True,
)[0]
self.assertEqual(totals_after_cancel.total_debit, totals_after_cancel.total_credit)
def create_company():
company = frappe.get_doc(

View File

@@ -812,6 +812,7 @@
},
{
"default": "0",
"fetch_from": "item_code.grant_commission",
"fieldname": "grant_commission",
"fieldtype": "Check",
"label": "Grant Commission",
@@ -858,7 +859,7 @@
],
"istable": 1,
"links": [],
"modified": "2024-05-07 15:56:54.343317",
"modified": "2026-04-20 16:16:12.322024",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Item",

View File

@@ -202,15 +202,14 @@ class POSProfile(Document):
def set_defaults(self, include_current_pos=True):
frappe.defaults.clear_default("is_pos")
if not include_current_pos:
condition = " where pfu.name != '%s' and pfu.default = 1 " % self.name.replace("'", "'")
else:
condition = " where pfu.default = 1 "
pfu = frappe.qb.DocType("POS Profile User")
pos_view_users = frappe.db.sql_list(
f"""select pfu.user
from `tabPOS Profile User` as pfu {condition}"""
)
query = frappe.qb.from_(pfu).select(pfu.user).where(pfu.default == 1)
if not include_current_pos:
query = query.where(pfu.name != self.name)
pos_view_users = query.run(as_list=1, pluck=True)
for user in pos_view_users:
if user:
@@ -309,32 +308,3 @@ def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
)
return pos_profile
@frappe.whitelist()
def set_default_profile(pos_profile, company):
modified = now()
user = frappe.session.user
if pos_profile and company:
frappe.db.sql(
""" update `tabPOS Profile User` pfu, `tabPOS Profile` pf
set
pfu.default = 0, pf.modified = %s, pf.modified_by = %s
where
pfu.user = %s and pf.name = pfu.parent and pf.company = %s
and pfu.default = 1""",
(modified, user, user, company),
auto_commit=1,
)
frappe.db.sql(
""" update `tabPOS Profile User` pfu, `tabPOS Profile` pf
set
pfu.default = 1, pf.modified = %s, pf.modified_by = %s
where
pfu.user = %s and pf.name = pfu.parent and pf.company = %s and pf.name = %s
""",
(modified, user, user, company, pos_profile),
auto_commit=1,
)

View File

@@ -243,8 +243,10 @@ def get_other_conditions(conditions, values, args):
if group_condition:
conditions += " and " + group_condition
date = args.get("transaction_date") or frappe.get_value(
args.get("doctype"), args.get("name"), "posting_date", ignore=True
date = (
args.get("transaction_date")
or args.get("posting_date")
or frappe.get_value(args.get("doctype"), args.get("name"), "posting_date", ignore=True)
)
if date:
conditions += """ and %(transaction_date)s between ifnull(`tabPricing Rule`.valid_from, '2000-01-01')
@@ -656,7 +658,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
if pricing_rule.is_recursive:
transaction_qty = sum(
[
row.qty
flt(row.qty)
for row in doc.items
if not row.is_free_item
and row.item_code == args.item_code

View File

@@ -151,13 +151,13 @@
"label": "Default Advance Account",
"mandatory_depends_on": "doc.party_type",
"options": "Account",
"reqd": 1
"reqd": 0
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2025-01-08 08:22:14.798085",
"modified": "2026-05-16 11:43:12.758685",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Payment Reconciliation",

View File

@@ -23,7 +23,7 @@ class ProcessPaymentReconciliation(Document):
bank_cash_account: DF.Link | None
company: DF.Link
cost_center: DF.Link | None
default_advance_account: DF.Link
default_advance_account: DF.Link | None
error_log: DF.LongText | None
from_invoice_date: DF.Date | None
from_payment_date: DF.Date | None
@@ -128,6 +128,7 @@ def is_job_running(job_name: str) -> bool:
@frappe.whitelist()
def pause_job_for_doc(docname: str | None = None):
if docname:
frappe.has_permission("Process Payment Reconciliation", "write", doc=docname, throw=True)
frappe.db.set_value("Process Payment Reconciliation", docname, "status", "Paused")
log = frappe.db.get_value("Process Payment Reconciliation Log", filters={"process_pr": docname})
if log:
@@ -142,6 +143,8 @@ def trigger_job_for_doc(docname: str | None = None):
if not docname:
return
frappe.has_permission("Process Payment Reconciliation", "write", doc=docname, throw=True)
if not frappe.db.get_single_value("Accounts Settings", "auto_reconcile_payments"):
frappe.throw(
_("Auto Reconciliation of Payments has been disabled. Enable it through {0}").format(
@@ -215,10 +218,7 @@ def trigger_reconciliation_for_queued_docs():
fields = ["company", "party_type", "party", "receivable_payable_account", "default_advance_account"]
def get_filters_as_tuple(fields, doc):
filters = ()
for x in fields:
filters += tuple(doc.get(x))
return filters
return tuple(doc.get(x) or "" for x in fields)
for x in all_queued:
doc = frappe.get_doc("Process Payment Reconciliation", x)

View File

@@ -1,5 +1,6 @@
{
"actions": [],
"allow_bulk_edit": 1,
"autoname": "format:Process-PCV-{###}",
"creation": "2025-09-25 15:44:03.534699",
"doctype": "DocType",
@@ -7,11 +8,13 @@
"field_order": [
"parent_pcv",
"status",
"amended_from",
"section_normal_balances",
"p_l_closing_balance",
"normal_balances",
"bs_closing_balance",
"z_opening_balances",
"amended_from"
"normal_balances",
"section_opening_balances",
"z_opening_balances"
],
"fields": [
{
@@ -64,17 +67,27 @@
"fieldname": "bs_closing_balance",
"fieldtype": "JSON",
"label": "Balance Sheet Closing Balance"
},
{
"fieldname": "section_normal_balances",
"fieldtype": "Tab Break",
"label": "Normal Balances"
},
{
"fieldname": "section_opening_balances",
"fieldtype": "Tab Break",
"label": "Opening Balances"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2025-11-05 11:40:24.996403",
"modified": "2026-06-01 12:16:37.374412",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Period Closing Voucher",
"naming_rule": "Expression",
"naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{

View File

@@ -69,8 +69,8 @@ class ProcessPeriodClosingVoucher(Document):
pcv = frappe.get_doc("Period Closing Voucher", self.parent_pcv)
if pcv.is_first_period_closing_voucher():
gl = qb.DocType("GL Entry")
min = qb.from_(gl).select(Min(gl.posting_date)).where(gl.company.eq(pcv.company)).run()[0][0]
max = qb.from_(gl).select(Max(gl.posting_date)).where(gl.company.eq(pcv.company)).run()[0][0]
min = qb.from_(gl).select(Min(gl.posting_date)).run()[0][0]
max = qb.from_(gl).select(Max(gl.posting_date)).run()[0][0]
dates = self.get_dates(get_datetime(min), get_datetime(max))
for x in dates:
@@ -89,13 +89,18 @@ class ProcessPeriodClosingVoucher(Document):
@frappe.whitelist()
def start_pcv_processing(docname: str):
if frappe.db.get_value("Process Period Closing Voucher", docname, "status") in ["Queued", "Running"]:
frappe.has_permission("Process Payment Reconciliation", "write", doc=docname, throw=True)
frappe.db.set_value("Process Period Closing Voucher", docname, "status", "Running")
if normal_balances := frappe.db.get_all(
"Process Period Closing Voucher Detail",
filters={"parent": docname, "status": "Queued"},
fields=["processing_date", "report_type", "parentfield"],
order_by="parentfield, idx, processing_date",
limit=4,
ppcvd = qb.DocType("Process Period Closing Voucher Detail")
if normal_balances := (
qb.from_(ppcvd)
.select(ppcvd.processing_date, ppcvd.report_type, ppcvd.parentfield)
.where(ppcvd.parent.eq(docname) & ppcvd.status.eq("Queued"))
.orderby(ppcvd.parentfield, ppcvd.idx, ppcvd.processing_date)
.limit(4)
.for_update(skip_locked=True)
.run(as_dict=True)
):
if not is_scheduler_inactive():
for x in normal_balances:
@@ -130,9 +135,10 @@ def pause_pcv_processing(docname: str):
ppcv = qb.DocType("Process Period Closing Voucher")
qb.update(ppcv).set(ppcv.status, "Paused").where(ppcv.name.eq(docname)).run()
# If a date is stuck in 'Running' state, this will allow it to procced.
if queued_dates := frappe.db.get_all(
"Process Period Closing Voucher Detail",
filters={"parent": docname, "status": "Queued"},
filters={"parent": docname, "status": ["in", ["Queued", "Running"]]},
pluck="name",
):
ppcvd = qb.DocType("Process Period Closing Voucher Detail")
@@ -166,6 +172,9 @@ def resume_pcv_processing(docname: str):
ppcvd = qb.DocType("Process Period Closing Voucher Detail")
qb.update(ppcvd).set(ppcvd.status, "Queued").where(ppcvd.name.isin(paused_dates)).run()
start_pcv_processing(docname)
else:
# If a parent doc is stuck in 'Running' state, will allow it to proceed.
schedule_next_date(docname)
def update_default_dimensions(dimension_fields, gl_entry, dimension_values):
@@ -235,12 +244,15 @@ def get_gle_for_closing_account(pcv, dimension_balance, dimensions):
@frappe.whitelist()
def schedule_next_date(docname: str):
if to_process := frappe.db.get_all(
"Process Period Closing Voucher Detail",
filters={"parent": docname, "status": "Queued"},
fields=["processing_date", "report_type", "parentfield"],
order_by="parentfield, idx, processing_date",
limit=1,
ppcvd = qb.DocType("Process Period Closing Voucher Detail")
if to_process := (
qb.from_(ppcvd)
.select(ppcvd.processing_date, ppcvd.report_type, ppcvd.parentfield)
.where(ppcvd.parent.eq(docname) & ppcvd.status.eq("Queued"))
.orderby(ppcvd.parentfield, ppcvd.idx, ppcvd.processing_date)
.limit(1)
.for_update(skip_locked=True)
.run(as_dict=True)
):
if not is_scheduler_inactive():
frappe.db.set_value(
@@ -278,7 +290,21 @@ def schedule_next_date(docname: str):
)
# Ensure both normal and opening balances are processed for all dates
if total_no_of_dates == completed:
summarize_and_post_ledger_entries(docname)
from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import (
is_job_running,
)
job_name = f"summarize_{docname}"
if not is_job_running(job_name):
frappe.enqueue(
method="erpnext.accounts.doctype.process_period_closing_voucher.process_period_closing_voucher.summarize_and_post_ledger_entries",
queue="long",
timeout="3600",
is_async=True,
job_name=job_name,
enqueue_after_commit=True,
docname=docname,
)
def make_dict_json_compliant(dimension_wise_balance) -> dict:
@@ -534,6 +560,9 @@ def process_individual_date(docname: str, date, report_type, parentfield):
if parentfield == "z_opening_balances":
query = query.where(gle.is_opening.eq("Yes"))
else:
# Keep balances aligned with legacy PCV logic (non-opening transactions only)
query = query.where(gle.is_opening.eq("No"))
query = query.groupby(gle.account)
for dim in dimensions:

View File

@@ -0,0 +1,17 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
// render
frappe.listview_settings["Process Period Closing Voucher"] = {
add_fields: ["status"],
get_indicator: function (doc) {
const status_colors = {
Queued: "blue",
Running: "orange",
Paused: "gray",
Completed: "green",
Cancelled: "red",
};
return [__(doc.status), status_colors[doc.status], "status,=," + doc.status];
},
};

View File

@@ -99,6 +99,7 @@ class ProcessStatementOfAccounts(Document):
validate_template(self.subject)
validate_template(self.body)
validate_template(self.pdf_name)
if not self.customers:
frappe.throw(_("Customers not selected."))
@@ -492,6 +493,7 @@ def download_statements(document_name):
@frappe.whitelist()
def send_emails(document_name, from_scheduler=False, posting_date=None):
doc = frappe.get_doc("Process Statement Of Accounts", document_name)
doc.check_permission()
report = get_report_pdf(doc, consolidated=False)
if report:
@@ -548,6 +550,7 @@ def send_emails(document_name, from_scheduler=False, posting_date=None):
@frappe.whitelist()
def send_auto_email():
frappe.has_permission("Process Statement Of Accounts", throw=True)
selected = frappe.get_list(
"Process Statement Of Accounts",
filters={"enable_auto_email": 1},

View File

@@ -21,10 +21,12 @@ frappe.ui.form.on("Promotional Scheme", {
selling: function (frm) {
frm.trigger("set_options_for_applicable_for");
frm.toggle_enable("buying", !frm.doc.selling);
},
buying: function (frm) {
frm.trigger("set_options_for_applicable_for");
frm.toggle_enable("selling", !frm.doc.buying);
},
set_options_for_applicable_for: function (frm) {

View File

@@ -115,7 +115,12 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
}
}
if (doc.docstatus == 1 && doc.outstanding_amount != 0 && !doc.on_hold) {
if (
doc.docstatus == 1 &&
doc.outstanding_amount != 0 &&
!doc.on_hold &&
frappe.model.can_create("Payment Entry")
) {
this.frm.add_custom_button(__("Payment"), () => this.make_payment_entry(), __("Create"));
cur_frm.page.set_inner_btn_group_as_primary(__("Create"));
}
@@ -126,7 +131,13 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
}
}
if (doc.docstatus == 1 && doc.outstanding_amount > 0 && !cint(doc.is_return) && !doc.on_hold) {
if (
doc.docstatus == 1 &&
doc.outstanding_amount > 0 &&
!cint(doc.is_return) &&
!doc.on_hold &&
frappe.boot.user.in_create.includes("Payment Request")
) {
this.frm.add_custom_button(
__("Payment Request"),
function () {
@@ -460,13 +471,14 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
}
items_add(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
this.frm.script_manager.copy_from_first_row("items", row, [
"expense_account",
"discount_account",
"cost_center",
"project",
]);
const row = frappe.get_doc(cdt, cdn);
const field_copy = ["expense_account", "discount_account", "cost_center"];
if (doc.project) {
frappe.model.set_value(cdt, cdn, "project", doc.project);
} else {
field_copy.push("project");
}
this.frm.script_manager.copy_from_first_row("items", row, field_copy);
}
on_submit() {
@@ -575,12 +587,6 @@ cur_frm.fields_dict["items"].grid.get_field("cost_center").get_query = function
};
};
cur_frm.fields_dict["items"].grid.get_field("project").get_query = function (doc, cdt, cdn) {
return {
filters: [["Project", "status", "not in", "Completed, Cancelled"]],
};
};
frappe.ui.form.on("Purchase Invoice", {
setup: function (frm) {
frm.custom_make_buttons = {
@@ -602,6 +608,25 @@ frappe.ui.form.on("Purchase Invoice", {
};
});
frm.set_query("write_off_account", function (doc) {
return {
filters: {
report_type: "Profit and Loss",
is_group: 0,
company: doc.company,
},
};
});
frm.set_query("write_off_cost_center", function (doc) {
return {
filters: {
is_group: 0,
company: doc.company,
},
};
});
frm.fields_dict["items"].grid.get_field("deferred_expense_account").get_query = function (doc) {
return {
filters: {

View File

@@ -312,7 +312,7 @@
"fieldname": "posting_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Date",
"label": "Posting Date",
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
"print_hide": 1,
@@ -382,7 +382,7 @@
},
{
"collapsible": 1,
"collapsible_depends_on": "bill_no",
"collapsible_depends_on": "posting_date",
"fieldname": "supplier_invoice_details",
"fieldtype": "Section Break",
"label": "Supplier Invoice"
@@ -1660,7 +1660,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2026-02-05 20:45:16.964500",
"modified": "2026-03-17 20:44:00.221219",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -287,6 +287,7 @@ class PurchaseInvoice(BuyingController):
self.validate_expense_account()
self.set_against_expense_account()
self.validate_write_off_account()
self.validate_write_off_cost_center()
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount")
self.set_status()
self.validate_purchase_receipt_if_update_stock()
@@ -612,12 +613,13 @@ class PurchaseInvoice(BuyingController):
frappe.db.set_value(self.doctype, self.name, "against_expense_account", self.against_expense_account)
def po_required(self):
if frappe.db.get_value("Buying Settings", None, "po_required") == "Yes":
if frappe.get_value(
if (
frappe.db.get_single_value("Buying Settings", "po_required") == "Yes"
and not self.is_internal_transfer()
and not frappe.db.get_value(
"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_order"
):
return
)
):
for d in self.get("items"):
if not d.purchase_order:
msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code))
@@ -632,15 +634,16 @@ class PurchaseInvoice(BuyingController):
throw(msg, title=_("Mandatory Purchase Order"))
def pr_required(self):
stock_items = self.get_stock_items()
if frappe.db.get_value("Buying Settings", None, "pr_required") == "Yes":
if frappe.db.get_single_value("Buying Settings", "pr_required") == "Yes":
stock_and_asset_items = self.get_stock_items()
stock_and_asset_items.extend(self.get_asset_items())
if frappe.get_value(
"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_receipt"
):
return
for d in self.get("items"):
if not d.purchase_receipt and d.item_code in stock_items:
if not d.purchase_receipt and d.item_code in stock_and_asset_items:
msg = _("Purchase Receipt Required for item {}").format(frappe.bold(d.item_code))
msg += "<br><br>"
msg += _(
@@ -656,6 +659,27 @@ class PurchaseInvoice(BuyingController):
if self.write_off_amount and not self.write_off_account:
throw(_("Please enter Write Off Account"))
if not self.write_off_account:
return
doc = frappe.db.get_value(
"Account", self.write_off_account, ["report_type", "is_group", "company"], as_dict=True
)
if not doc or doc.report_type != "Profit and Loss" or doc.is_group or doc.company != self.company:
throw(_("Please enter a valid Write Off Account"))
def validate_write_off_cost_center(self):
if not self.write_off_cost_center:
return
doc = frappe.db.get_value(
"Cost Center", self.write_off_cost_center, ["is_group", "company"], as_dict=True
)
if not doc or doc.is_group or doc.company != self.company:
throw(_("Please enter a valid Write Off Cost Center"))
def check_prev_docstatus(self):
for d in self.get("items"):
if d.purchase_order:
@@ -728,13 +752,15 @@ class PurchaseInvoice(BuyingController):
for item in self.get("items"):
if item.purchase_receipt:
frappe.throw(
_("Stock cannot be updated against Purchase Receipt {0}").format(
item.purchase_receipt
)
_(
"Stock cannot be updated for Purchase Invoice {0} because a Purchase Receipt {1} has already been created for this transaction. Please disable the 'Update Stock' checkbox in the Purchase Invoice and save the invoice."
).format(self.name, item.purchase_receipt),
title=_("Stock Update Not Allowed"),
)
def validate_for_repost(self):
self.validate_write_off_account()
self.validate_write_off_cost_center()
self.validate_expense_account()
validate_docs_for_voucher_types(["Purchase Invoice"])
validate_docs_for_deferred_accounting([], [self.name])
@@ -848,7 +874,9 @@ class PurchaseInvoice(BuyingController):
if update_outstanding == "No":
update_voucher_outstanding(
voucher_type=self.doctype,
voucher_no=self.return_against if cint(self.is_return) and self.return_against else self.name,
voucher_no=self.return_against
if (cint(self.is_return) and self.return_against)
else self.name,
account=self.credit_to,
party_type="Supplier",
party=self.supplier,
@@ -976,6 +1004,10 @@ class PurchaseInvoice(BuyingController):
if provisional_accounting_for_non_stock_items:
self.get_provisional_accounts()
adjust_incoming_rate = frappe.db.get_single_value(
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate"
)
for item in self.get("items"):
if flt(item.base_net_amount) or (self.get("update_stock") and item.valuation_rate):
if item.item_code:
@@ -1144,7 +1176,11 @@ class PurchaseInvoice(BuyingController):
)
# check if the exchange rate has changed
if item.get("purchase_receipt") and self.auto_accounting_for_stock:
if (
not adjust_incoming_rate
and item.get("purchase_receipt")
and self.auto_accounting_for_stock
):
if (
exchange_rate_map[item.purchase_receipt]
and self.conversion_rate != exchange_rate_map[item.purchase_receipt]
@@ -1181,6 +1217,7 @@ class PurchaseInvoice(BuyingController):
item=item,
)
)
if (
self.auto_accounting_for_stock
and self.is_opening == "No"
@@ -1522,6 +1559,9 @@ class PurchaseInvoice(BuyingController):
def make_payment_gl_entries(self, gl_entries):
# Make Cash GL Entries
if cint(self.is_paid) and self.cash_bank_account and self.paid_amount:
against_voucher = self.name
if self.is_return and self.return_against and not self.update_outstanding_for_self:
against_voucher = self.return_against
bank_account_currency = get_account_currency(self.cash_bank_account)
# CASH, make payment entries
gl_entries.append(
@@ -1536,9 +1576,7 @@ class PurchaseInvoice(BuyingController):
if self.party_account_currency == self.company_currency
else self.paid_amount,
"debit_in_transaction_currency": self.paid_amount,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
"project": self.project,
@@ -2048,6 +2086,7 @@ def make_stock_entry(source_name, target_doc=None):
def change_release_date(name, release_date=None):
if frappe.db.exists("Purchase Invoice", name):
pi = frappe.get_doc("Purchase Invoice", name)
pi.check_permission()
pi.db_set("release_date", release_date)

View File

@@ -356,6 +356,12 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
make_purchase_invoice as create_purchase_invoice,
)
original_value = frappe.db.get_single_value(
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate"
)
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
@@ -376,12 +382,17 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
amount = frappe.db.get_value(
"GL Entry", {"account": exchange_gain_loss_account, "voucher_no": pi.name}, "debit"
)
discrepancy_caused_by_exchange_rate_diff = abs(
pi.items[0].base_net_amount - pr.items[0].base_net_amount
)
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
frappe.db.set_single_value(
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", original_value
)
def test_purchase_invoice_with_exchange_rate_difference_for_non_stock_item(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
make_purchase_invoice as create_purchase_invoice,

View File

@@ -109,6 +109,7 @@
"sales_invoice_item",
"material_request",
"material_request_item",
"delivered_by_supplier",
"item_weight_details",
"weight_per_unit",
"total_weight",
@@ -731,7 +732,6 @@
"label": "Valuation Rate",
"no_copy": 1,
"options": "Company:company:default_currency",
"precision": "6",
"print_hide": 1,
"read_only": 1
},
@@ -979,12 +979,21 @@
"fieldtype": "Currency",
"label": "Distributed Discount Amount",
"options": "currency"
},
{
"default": "0",
"fieldname": "delivered_by_supplier",
"fieldtype": "Check",
"hidden": 1,
"label": "Delivered by Supplier",
"print_hide": 1,
"read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2025-10-14 13:01:54.441511",
"modified": "2026-05-06 08:08:40.782395",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@@ -31,6 +31,7 @@ class PurchaseInvoiceItem(Document):
conversion_factor: DF.Float
cost_center: DF.Link | None
deferred_expense_account: DF.Link | None
delivered_by_supplier: DF.Check
description: DF.TextEditor | None
discount_amount: DF.Currency
discount_percentage: DF.Percent

View File

@@ -154,12 +154,13 @@ class RepostAccountingLedger(Document):
@frappe.whitelist()
def start_repost(account_repost_doc=str) -> None:
def start_repost(account_repost_doc: str | None = None) -> None:
from erpnext.accounts.general_ledger import make_reverse_gl_entries
frappe.flags.through_repost_accounting_ledger = True
if account_repost_doc:
repost_doc = frappe.get_doc("Repost Accounting Ledger", account_repost_doc)
repost_doc.check_permission("write")
if repost_doc.docstatus == 1:
# Prevent repost on invoices with deferred accounting

View File

@@ -94,7 +94,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
}
if (doc.docstatus == 1 && doc.outstanding_amount != 0) {
if (doc.docstatus == 1 && doc.outstanding_amount != 0 && frappe.model.can_create("Payment Entry")) {
this.frm.add_custom_button(__("Payment"), () => this.make_payment_entry(), __("Create"));
this.frm.page.set_inner_btn_group_as_primary(__("Create"));
}
@@ -136,13 +136,15 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
}
if (doc.outstanding_amount > 0) {
cur_frm.add_custom_button(
__("Payment Request"),
function () {
me.make_payment_request();
},
__("Create")
);
if (frappe.boot.user.in_create.includes("Payment Request")) {
this.frm.add_custom_button(
__("Payment Request"),
function () {
me.make_payment_request();
},
__("Create")
);
}
this.frm.add_custom_button(
__("Invoice Discounting"),
this.make_invoice_discounting.bind(this),
@@ -166,13 +168,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
);
}
}
// Show buttons only when pos view is active
if (cint(doc.docstatus == 0) && cur_frm.page.current_view_name !== "pos" && !doc.is_return) {
this.frm.cscript.sales_order_btn();
this.frm.cscript.delivery_note_btn();
this.frm.cscript.quotation_btn();
}
this.toggle_get_items();
this.set_default_print_format();
if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
@@ -184,12 +180,31 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
: "Inter Company Purchase Invoice";
me.frm.add_custom_button(
button_label,
__(button_label),
function () {
me.make_inter_company_invoice();
},
__("Create")
);
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_received_items",
args: {
reference_name: me.frm.doc.name,
doctype: "Purchase Invoice",
reference_fieldname: "sales_invoice_item",
},
callback: function (r) {
if (r.exc) return;
const received_items = r.message || {};
const has_pending_qty = me.frm.doc.items.some(
(item) => flt(item.qty) - flt(received_items[item.name] || 0) > 0
);
if (!has_pending_qty) {
me.frm.remove_custom_button(__(button_label), __("Create"));
}
},
});
}
}
@@ -258,6 +273,93 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
}
}
toggle_get_items() {
const buttons = ["Sales Order", "Quotation", "Timesheet", "Delivery Note"];
buttons.forEach((label) => {
this.frm.remove_custom_button(label, "Get Items From");
});
if (cint(this.frm.doc.docstatus) !== 0 || this.frm.page.current_view_name === "pos") {
return;
}
if (!this.frm.doc.is_return) {
this.frm.cscript.sales_order_btn();
this.frm.cscript.quotation_btn();
this.frm.cscript.timesheet_btn();
}
this.frm.cscript.delivery_note_btn();
}
timesheet_btn() {
var me = this;
me.frm.add_custom_button(
__("Timesheet"),
function () {
let d = new frappe.ui.Dialog({
title: __("Fetch Timesheet"),
fields: [
{
label: __("From"),
fieldname: "from_time",
fieldtype: "Date",
reqd: 1,
},
{
label: __("Item Code"),
fieldname: "item_code",
fieldtype: "Link",
options: "Item",
get_query: () => {
return {
query: "erpnext.controllers.queries.item_query",
filters: {
is_sales_item: 1,
customer: me.frm.doc.customer,
has_variants: 0,
},
};
},
},
{
fieldtype: "Column Break",
fieldname: "col_break_1",
},
{
label: __("To"),
fieldname: "to_time",
fieldtype: "Date",
reqd: 1,
},
{
label: __("Project"),
fieldname: "project",
fieldtype: "Link",
options: "Project",
default: me.frm.doc.project,
},
],
primary_action: function () {
const data = d.get_values();
me.frm.events.add_timesheet_data(me.frm, {
from_time: data.from_time,
to_time: data.to_time,
project: data.project,
item_code: data.item_code,
});
d.hide();
},
primary_action_label: __("Get Timesheets"),
});
d.show();
},
__("Get Items From")
);
}
sales_order_btn() {
var me = this;
this.$sales_order_btn = this.frm.add_custom_button(
@@ -322,6 +424,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
this.$delivery_note_btn = this.frm.add_custom_button(
__("Delivery Note"),
function () {
if (!me.frm.doc.customer) {
frappe.throw({
title: __("Mandatory"),
message: __("Please Select a Customer"),
});
}
erpnext.utils.map_current_doc({
method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice",
source_doctype: "Delivery Note",
@@ -334,7 +442,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
var filters = {
docstatus: 1,
company: me.frm.doc.company,
is_return: 0,
is_return: me.frm.doc.is_return,
};
if (me.frm.doc.customer) filters["customer"] = me.frm.doc.customer;
return {
@@ -453,12 +561,14 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
}
items_add(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
this.frm.script_manager.copy_from_first_row("items", row, [
"income_account",
"discount_account",
"cost_center",
]);
const row = frappe.get_doc(cdt, cdn);
const field_copy = ["income_account", "discount_account", "cost_center"];
if (doc.project) {
frappe.model.set_value(cdt, cdn, "project", doc.project);
} else {
field_copy.push("project");
}
this.frm.script_manager.copy_from_first_row("items", row, field_copy);
}
set_dynamic_labels() {
@@ -594,6 +704,14 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
this.calculate_taxes_and_totals();
}
apply_tds(frm) {
this.frm.clear_table("tax_withholding_entries");
}
is_return() {
this.toggle_get_items();
}
};
// for backward compatibility: combine new and previous states
@@ -1039,71 +1157,6 @@ frappe.ui.form.on("Sales Invoice", {
},
refresh: function (frm) {
if (frm.doc.docstatus === 0 && !frm.doc.is_return) {
frm.add_custom_button(
__("Timesheet"),
function () {
let d = new frappe.ui.Dialog({
title: __("Fetch Timesheet"),
fields: [
{
label: __("From"),
fieldname: "from_time",
fieldtype: "Date",
reqd: 1,
},
{
label: __("Item Code"),
fieldname: "item_code",
fieldtype: "Link",
options: "Item",
get_query: () => {
return {
query: "erpnext.controllers.queries.item_query",
filters: {
is_sales_item: 1,
customer: frm.doc.customer,
has_variants: 0,
},
};
},
},
{
fieldtype: "Column Break",
fieldname: "col_break_1",
},
{
label: __("To"),
fieldname: "to_time",
fieldtype: "Date",
reqd: 1,
},
{
label: __("Project"),
fieldname: "project",
fieldtype: "Link",
options: "Project",
default: frm.doc.project,
},
],
primary_action: function () {
const data = d.get_values();
frm.events.add_timesheet_data(frm, {
from_time: data.from_time,
to_time: data.to_time,
project: data.project,
item_code: data.item_code,
});
d.hide();
},
primary_action_label: __("Get Timesheets"),
});
d.show();
},
__("Get Items From")
);
}
if (frm.doc.is_debit_note) {
frm.set_df_property("return_against", "label", __("Adjustment Against"));
}

View File

@@ -373,7 +373,7 @@
"fieldtype": "Date",
"hide_days": 1,
"hide_seconds": 1,
"label": "Date",
"label": "Posting Date",
"no_copy": 1,
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
@@ -777,8 +777,7 @@
},
{
"collapsible": 1,
"collapsible_depends_on": "eval:doc.total_billing_amount > 0",
"depends_on": "eval:!doc.is_return",
"collapsible_depends_on": "eval:doc.total_billing_amount > 0 || doc.total_billing_hours > 0",
"fieldname": "time_sheet_list",
"fieldtype": "Section Break",
"hide_border": 1,
@@ -792,7 +791,6 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Time Sheets",
"no_copy": 1,
"options": "Sales Invoice Timesheet",
"print_hide": 1
},
@@ -2112,7 +2110,7 @@
"fieldtype": "Column Break"
},
{
"depends_on": "eval:(!doc.is_return && doc.total_billing_amount > 0)",
"depends_on": "eval:doc.total_billing_amount > 0 || doc.total_billing_hours > 0",
"fieldname": "section_break_104",
"fieldtype": "Section Break"
},
@@ -2200,7 +2198,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2026-02-05 20:43:44.732805",
"modified": "2026-04-06 22:30:28.513139",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -23,7 +23,12 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category
get_party_tax_withholding_details,
)
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
from erpnext.accounts.party import get_due_date, get_party_account, get_party_details
from erpnext.accounts.party import (
CROSS_PARTY_FIELD_NO_MAP,
get_due_date,
get_party_account,
get_party_details,
)
from erpnext.accounts.utils import (
cancel_exchange_gain_loss_journal,
get_account_currency,
@@ -323,10 +328,22 @@ class SalesInvoice(SellingController):
)
self.set_against_income_account()
self.validate_time_sheets_are_submitted()
if self.is_return and not self.return_against and self.timesheets:
frappe.throw(_("Direct return is not allowed for Timesheet."))
if not self.is_return:
self.validate_time_sheets_are_submitted()
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount")
if self.is_return:
self.timesheets = []
if self.is_return and self.return_against:
for row in self.timesheets:
if row.billing_hours:
row.billing_hours = -abs(row.billing_hours)
if row.billing_amount:
row.billing_amount = -abs(row.billing_amount)
self.update_packing_list()
self.set_billing_hours_and_amount()
self.update_timesheet_billing_for_project()
@@ -494,7 +511,7 @@ class SalesInvoice(SellingController):
if not cint(self.is_pos) == 1 and not self.is_return:
self.update_against_document_in_jv()
self.update_time_sheet(self.name)
self.update_time_sheet(None if (self.is_return and self.return_against) else self.name)
if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
update_company_current_month_sales(self.company)
@@ -550,7 +567,7 @@ class SalesInvoice(SellingController):
self.check_if_consolidated_invoice()
super().before_cancel()
self.update_time_sheet(None)
self.update_time_sheet(self.return_against if (self.is_return and self.return_against) else None)
def on_cancel(self):
check_if_return_invoice_linked_with_payment_entry(self)
@@ -735,8 +752,20 @@ class SalesInvoice(SellingController):
for data in timesheet.time_logs:
if (
(self.project and args.timesheet_detail == data.name)
or (not self.project and not data.sales_invoice)
or (not sales_invoice and data.sales_invoice == self.name)
or (not self.project and not data.sales_invoice and args.timesheet_detail == data.name)
or (
not sales_invoice
and data.sales_invoice == self.name
and args.timesheet_detail == data.name
)
or (
self.is_return
and self.return_against
and data.sales_invoice
and data.sales_invoice == self.return_against
and not sales_invoice
and args.timesheet_detail == data.name
)
):
data.sales_invoice = sales_invoice
@@ -776,11 +805,25 @@ class SalesInvoice(SellingController):
payment.account = get_bank_cash_account(payment.mode_of_payment, self.company).get("account")
def validate_time_sheets_are_submitted(self):
# Note: This validation is skipped for return invoices
# to allow returns to reference already-billed timesheet details
for data in self.timesheets:
# Handle invoice duplication
if data.time_sheet and data.timesheet_detail:
if sales_invoice := frappe.db.get_value(
"Timesheet Detail", data.timesheet_detail, "sales_invoice"
):
frappe.throw(
_("Row {0}: Sales Invoice {1} is already created for {2}").format(
data.idx, frappe.bold(sales_invoice), frappe.bold(data.time_sheet)
)
)
if data.time_sheet:
status = frappe.db.get_value("Timesheet", data.time_sheet, "status")
if status not in ["Submitted", "Payslip"]:
frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet))
if status not in ["Submitted", "Payslip", "Partially Billed"]:
frappe.throw(
_("Timesheet {0} cannot be invoiced in its current state").format(data.time_sheet)
)
def set_pos_fields(self, for_validate=False):
"""Set retail related fields from POS Profiles"""
@@ -804,11 +847,9 @@ class SalesInvoice(SellingController):
if self.pos_profile:
pos = frappe.get_doc("POS Profile", self.pos_profile)
if not self.get("payments") and not for_validate:
update_multi_mode_option(self, pos)
if pos:
if not for_validate:
update_multi_mode_option(self, pos)
self.tax_category = pos.get("tax_category")
if not for_validate and not self.customer:
@@ -1114,7 +1155,12 @@ class SalesInvoice(SellingController):
timesheet.billing_amount = ts_doc.total_billable_amount
def update_timesheet_billing_for_project(self):
if not self.timesheets and self.project and self.is_auto_fetch_timesheet_enabled():
if (
not self.is_return
and not self.timesheets
and self.project
and self.is_auto_fetch_timesheet_enabled()
):
self.add_timesheet_data()
else:
self.calculate_billing_amount_for_timesheet()
@@ -1199,6 +1245,9 @@ class SalesInvoice(SellingController):
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
def process_asset_depreciation(self):
if self.is_internal_transfer():
return
if (self.is_return and self.docstatus == 2) or (not self.is_return and self.docstatus == 1):
self.depreciate_asset_on_sale()
else:
@@ -2482,7 +2531,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
"rate": "rate",
},
"postprocess": update_item,
"condition": lambda doc: doc.qty > 0,
"condition": lambda doc: doc.qty - received_items.get(doc.name, 0.0) > 0,
}
if doctype in ["Sales Invoice", "Sales Order"]:
@@ -2515,18 +2564,25 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
"doctype": target_doctype,
"postprocess": update_details,
"set_target_warehouse": "set_from_warehouse",
"field_no_map": ["taxes_and_charges", "set_warehouse", "shipping_address"],
"field_no_map": [*CROSS_PARTY_FIELD_NO_MAP, "set_warehouse", "cost_center"],
},
doctype + " Item": item_field_map,
},
target_doc,
set_missing_values,
)
if not doclist.get("items"):
frappe.throw(
_(
"Cannot create Intercompany {0}. All items in the source {1} have already been fully invoiced. "
"Please check the existing linked {2}s."
).format(target_doctype, doctype, target_doctype)
)
return doclist
def get_received_items(reference_name, doctype, reference_fieldname):
@frappe.whitelist()
def get_received_items(reference_name: str, doctype: str, reference_fieldname: str):
reference_field = "inter_company_invoice_reference"
if doctype == "Purchase Order":
reference_field = "inter_company_order_reference"
@@ -2539,20 +2595,19 @@ def get_received_items(reference_name, doctype, reference_fieldname):
target_doctypes = frappe.get_all(
doctype,
filters=filters,
as_list=True,
pluck="name",
)
received_items_map = {}
if target_doctypes:
target_doctypes = list(target_doctypes[0])
received_items_map = frappe._dict(
frappe.get_all(
received_items_data = frappe.get_all(
doctype + " Item",
filters={"parent": ("in", target_doctypes)},
fields=[reference_fieldname, "qty"],
as_list=1,
)
)
for item in received_items_data:
key = item.get(reference_fieldname)
if key:
received_items_map[key] = received_items_map.get(key, 0.0) + flt(item.qty)
return received_items_map
@@ -2744,6 +2799,8 @@ def update_multi_mode_option(doc, pos_profile):
payment.account = payment_mode.default_account
payment.type = payment_mode.type
mop_refetched = bool(doc.payments)
doc.set("payments", [])
invalid_modes = []
mode_of_payments = [d.mode_of_payment for d in pos_profile.get("payments")]
@@ -2765,6 +2822,11 @@ def update_multi_mode_option(doc, pos_profile):
msg = _("Please set default Cash or Bank account in Mode of Payments {}")
frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
if mop_refetched:
frappe.msgprint(
_("Payment methods refreshed. Please review before proceeding."), indicator="orange", alert=True
)
def get_all_mode_of_payments(doc):
return frappe.db.sql(

View File

@@ -2690,6 +2690,95 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(target_doc.company, "_Test Company 1")
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
def test_restrict_inter_company_pi_when_sales_invoice_qty_fully_consumed(self):
item_code_1 = "_Test IC Item 1"
item_code_2 = "_Test IC Item 2"
create_item(item_code_1, is_stock_item=1)
create_item(item_code_2, is_stock_item=1)
si = create_sales_invoice(
company="Wind Power LLC",
customer="_Test Internal Customer",
item_code=item_code_1,
debit_to="Debtors - WP",
warehouse="Stores - WP",
income_account="Sales - WP",
expense_account="Cost of Goods Sold - WP",
cost_center="Main - WP",
currency="USD",
qty=3,
do_not_save=1,
)
si.selling_price_list = "_Test Price List Rest of the World"
si.append(
"items",
{
"item_code": item_code_2,
"item_name": item_code_2,
"description": item_code_2,
"warehouse": "Stores - WP",
"qty": 2,
"uom": "Nos",
"stock_uom": "Nos",
"rate": 100,
"price_list_rate": 100,
"income_account": "Sales - WP",
"expense_account": "Cost of Goods Sold - WP",
"cost_center": "Main - WP",
"conversion_factor": 1,
},
)
si.submit()
target_doc = make_inter_company_transaction("Sales Invoice", si.name)
for item in target_doc.items:
item.update(
{
"expense_account": "Cost of Goods Sold - _TC1",
"cost_center": "Main - _TC1",
}
)
target_doc.submit()
self.assertEqual(len(target_doc.items), 2)
self.assertEqual([item.qty for item in target_doc.items], [3, 2])
with self.assertRaisesRegex(
frappe.ValidationError,
"already been fully invoiced",
):
make_inter_company_transaction("Sales Invoice", si.name)
def test_inter_company_transaction_does_not_inherit_party_fields(self):
"""
Party-derived fields on SI (from Customer) must not leak into the mapped PI.
"""
si = create_sales_invoice(
company="Wind Power LLC",
customer="_Test Internal Customer",
debit_to="Debtors - WP",
warehouse="Stores - WP",
income_account="Sales - WP",
expense_account="Cost of Goods Sold - WP",
cost_center="Main - WP",
currency="USD",
do_not_save=1,
)
si.selling_price_list = "_Test Price List Rest of the World"
si.tax_category = "_Test Tax Category 1"
si.language = "ar"
si.payment_terms_template = "_Test Payment Term Template"
si.submit()
pi = make_inter_company_transaction("Sales Invoice", si.name)
supplier = frappe.get_doc("Supplier", "_Test Internal Supplier")
self.assertEqual(pi.tax_category or None, supplier.tax_category or None)
self.assertEqual(pi.language or None, supplier.language or None)
self.assertEqual(pi.payment_terms_template or None, supplier.payment_terms or None)
def test_inter_company_transaction_without_default_warehouse(self):
"Check mapping (expense account) of inter company SI to PI in absence of default warehouse."
# setup
@@ -2917,7 +3006,7 @@ class TestSalesInvoice(FrappeTestCase):
si.submit()
# Check if adjustment entry is created
self.assertTrue(
self.assertFalse(
frappe.db.exists(
"GL Entry",
{
@@ -3230,7 +3319,7 @@ class TestSalesInvoice(FrappeTestCase):
calculate_depreciation=1,
submit=1,
)
post_depreciation_entries()
post_depreciation_entries(date="2025-04-01")
si = create_sales_invoice(
item_code="Macbook Pro", asset=asset.name, qty=1, rate=10000, posting_date=getdate("2025-05-01")
@@ -4835,6 +4924,33 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(stock_ledger_entry.incoming_rate, 0.0)
def test_inter_company_transaction_cost_center(self):
si = create_sales_invoice(
company="Wind Power LLC",
customer="_Test Internal Customer",
debit_to="Debtors - WP",
warehouse="Stores - WP",
income_account="Sales - WP",
expense_account="Cost of Goods Sold - WP",
parent_cost_center="Main - WP",
cost_center="Main - WP",
currency="USD",
do_not_save=1,
)
si.selling_price_list = "_Test Price List Rest of the World"
si.submit()
cost_center = frappe.db.get_value("Company", "_Test Company 1", "cost_center")
frappe.db.set_value("Company", "_Test Company 1", "cost_center", None)
target_doc = make_inter_company_transaction("Sales Invoice", si.name)
self.assertEqual(target_doc.cost_center, None)
self.assertEqual(target_doc.items[0].cost_center, None)
frappe.db.set_value("Company", "_Test Company 1", "cost_center", cost_center)
def make_item_for_si(item_code, properties=None):
from erpnext.stock.doctype.item.test_item import make_item

View File

@@ -52,7 +52,6 @@
"fieldtype": "Data",
"hidden": 1,
"label": "Timesheet Detail",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
@@ -117,7 +116,7 @@
],
"istable": 1,
"links": [],
"modified": "2021-10-02 03:48:44.979777",
"modified": "2026-04-06 22:30:28.513139",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Timesheet",

View File

@@ -25,6 +25,10 @@ frappe.ui.form.on("Shipping Rule", {
},
calculate_based_on: function (frm) {
frm.trigger("toggle_reqd");
if (frm.doc.calculate_based_on === "Fixed") {
frm.clear_table("conditions");
frm.refresh_field("conditions");
}
},
toggle_reqd: function (frm) {
frm.toggle_reqd("shipping_amount", frm.doc.calculate_based_on === "Fixed");

View File

@@ -58,6 +58,11 @@ class ShippingRule(Document):
self.validate_overlapping_shipping_rule_conditions()
def validate_from_to_values(self):
if self.calculate_based_on == "Fixed":
if self.conditions:
self.set("conditions", [])
return
zero_to_values = []
for d in self.get("conditions"):
@@ -152,7 +157,9 @@ class ShippingRule(Document):
frappe.throw(_("Shipping rule only applicable for Buying"))
shipping_charge["doctype"] = "Purchase Taxes and Charges"
shipping_charge["category"] = "Valuation and Total"
shipping_charge["category"] = (
"Valuation and Total" if doc.get_stock_items() or doc.get_asset_items() else "Total"
)
shipping_charge["add_deduct_tax"] = "Add"
existing_shipping_charge = doc.get("taxes", filters=shipping_charge)

View File

@@ -629,18 +629,21 @@ def create_parties():
customer.customer_name = "_Test Subscription Customer"
customer.default_currency = "USD"
customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable USD - _TC"})
customer.customer_group = "Individual"
customer.insert()
if not frappe.db.exists("Customer", "_Test Subscription Customer Multi Currency"):
customer = frappe.new_doc("Customer")
customer.customer_name = "Test Subscription Customer Multi Currency"
customer.default_currency = "USD"
customer.customer_group = "Individual"
customer.insert()
if not frappe.db.exists("Customer", "_Test Subscription Customer John Doe"):
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test Subscription Customer John Doe"
customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable - _TC"})
customer.customer_group = "Individual"
customer.insert()

View File

@@ -8,10 +8,13 @@ import frappe
from frappe import _
from frappe.contacts.doctype.address.address import get_default_address
from frappe.model.document import Document
from frappe.query_builder import DocType
from frappe.query_builder.functions import IfNull
from frappe.utils import cstr
from frappe.utils.nestedset import get_root_of
from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
from erpnext.setup.doctype.supplier_group.supplier_group import get_parent_supplier_groups
class IncorrectCustomerGroup(frappe.ValidationError):
@@ -174,38 +177,44 @@ def get_party_details(party, party_type, args=None):
def get_tax_template(posting_date, args):
"""Get matching tax rule"""
args = frappe._dict(args)
conditions = []
TaxRule = DocType("Tax Rule")
query = frappe.qb.from_(TaxRule).select("*")
if posting_date:
conditions.append(
f"""(from_date is null or from_date <= '{posting_date}')
and (to_date is null or to_date >= '{posting_date}')"""
query = query.where(
(TaxRule.from_date.isnull() | (TaxRule.from_date <= posting_date))
& (TaxRule.to_date.isnull() | (TaxRule.to_date >= posting_date))
)
else:
conditions.append("(from_date is null) and (to_date is null)")
query = query.where(TaxRule.from_date.isnull() & TaxRule.to_date.isnull())
conditions.append(
"ifnull(tax_category, '') = {}".format(frappe.db.escape(cstr(args.get("tax_category")), False))
)
if "tax_category" in args.keys():
del args["tax_category"]
def get_group_ancestors(doctype, get_parents, value):
if not value:
value = get_root_of(doctype)
return [""] + [d.name for d in get_parents(value)]
group_fields = {
"customer_group": ("Customer Group", get_parent_customer_groups),
"supplier_group": ("Supplier Group", get_parent_supplier_groups),
}
args.setdefault("tax_category", "")
for key, value in args.items():
if key == "use_for_shopping_cart":
conditions.append(f"use_for_shopping_cart = {1 if value else 0}")
elif key == "customer_group":
if not value:
value = get_root_of("Customer Group")
customer_group_condition = get_customer_group_condition(value)
conditions.append(f"ifnull({key}, '') in ('', {customer_group_condition})")
query = query.where(TaxRule.use_for_shopping_cart == value)
elif key == "tax_category":
query = query.where(IfNull(TaxRule.tax_category, "") == (value or ""))
elif key in group_fields:
doctype, get_parents = group_fields[key]
query = query.where(
IfNull(TaxRule[key], "").isin(get_group_ancestors(doctype, get_parents, value))
)
else:
conditions.append(f"ifnull({key}, '') in ('', {frappe.db.escape(cstr(value))})")
query = query.where(IfNull(TaxRule[key], "").isin(["", value or ""]))
tax_rule = frappe.db.sql(
"""select * from `tabTax Rule`
where {}""".format(" and ".join(conditions)),
as_dict=True,
)
tax_rule = query.run(as_dict=True)
if not tax_rule:
return None
@@ -234,11 +243,3 @@ def get_tax_template(posting_date, args):
return None
return tax_template
def get_customer_group_condition(customer_group):
condition = ""
customer_groups = ["%s" % (frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)]
if customer_groups:
condition = ",".join(["%s"] * len(customer_groups)) % (tuple(customer_groups))
return condition

View File

@@ -72,6 +72,117 @@ class TestTaxRule(unittest.TestCase):
"_Test Sales Taxes and Charges Template - _TC",
)
def test_for_parent_supplier_group(self):
purchase_template = "_Test Purchase Taxes and Charges Template - _TC"
if not frappe.db.exists("Purchase Taxes and Charges Template", purchase_template):
frappe.get_doc(
{
"doctype": "Purchase Taxes and Charges Template",
"title": "_Test Purchase Taxes and Charges Template",
"company": "_Test Company",
"taxes": [
{
"account_head": "_Test Account VAT - _TC",
"charge_type": "On Net Total",
"description": "VAT",
"doctype": "Purchase Taxes and Charges",
"cost_center": "Main - _TC",
"rate": 6,
}
],
}
).insert()
make_tax_rule(
supplier_group="All Supplier Groups",
tax_type="Purchase",
purchase_tax_template=purchase_template,
priority=1,
use_for_shopping_cart=0,
from_date="2015-01-01",
save=1,
)
# "_Test Supplier Group" has "All Supplier Groups" as its parent — should match hierarchically
self.assertEqual(
get_tax_template(
"2015-01-01",
{
"supplier_group": "_Test Supplier Group",
"tax_type": "Purchase",
"use_for_shopping_cart": 0,
},
),
purchase_template,
)
def test_use_for_shopping_cart_filter(self):
city = "Test Cart City"
# higher priority ensures this rule wins when use_for_shopping_cart is not filtered
make_tax_rule(
customer="_Test Customer",
billing_city=city,
sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
use_for_shopping_cart=0,
priority=2,
save=1,
)
make_tax_rule(
customer="_Test Customer",
billing_city=city,
sales_tax_template="_Test Sales Taxes and Charges Template 1 - _TC",
use_for_shopping_cart=1,
priority=1,
save=1,
)
# Cart request (use_for_shopping_cart=1) filters to cart rules only
self.assertEqual(
get_tax_template(
"2015-01-01",
{"customer": "_Test Customer", "billing_city": city, "use_for_shopping_cart": 1},
),
"_Test Sales Taxes and Charges Template 1 - _TC",
)
# Non-cart request omits use_for_shopping_cart — no filter is applied, both rules
# are candidates; non-cart rule wins by higher priority
self.assertEqual(
get_tax_template(
"2015-01-01",
{"customer": "_Test Customer", "billing_city": city},
),
"_Test Sales Taxes and Charges Template - _TC",
)
def test_use_for_shopping_cart_default(self):
city = "Test Default Cart City"
# use_for_shopping_cart not set — Check field defaults to 0
make_tax_rule(
customer="_Test Customer",
billing_city=city,
sales_tax_template="_Test Sales Taxes and Charges Template - _TC",
use_for_shopping_cart=0, # Default is set to 1.
save=1,
)
# Non-cart request (no use_for_shopping_cart in args) matches the rule
self.assertEqual(
get_tax_template(
"2015-01-01",
{"customer": "_Test Customer", "billing_city": city},
),
"_Test Sales Taxes and Charges Template - _TC",
)
# Cart request (use_for_shopping_cart=1) does not match — rule has default 0
self.assertIsNone(
get_tax_template(
"2015-01-01",
{"customer": "_Test Customer", "billing_city": city, "use_for_shopping_cart": 1},
)
)
def test_conflict_with_overlapping_dates(self):
tax_rule1 = make_tax_rule(
customer="_Test Customer",

View File

@@ -700,7 +700,12 @@ def make_reverse_gl_entries(
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
# For reverse entries, use the posting_date parameter if provided and valid
# Otherwise fall back to original posting_date
validation_date = posting_date if posting_date else gl_entries[0]["posting_date"]
validate_against_pcv(is_opening, validation_date, gl_entries[0]["company"])
if partial_cancel:
# Partial cancel is only used by `Advance` in separate account feature.
# Only cancel GL entries for unlinked reference using `voucher_detail_no`

View File

@@ -48,6 +48,25 @@ SALES_TRANSACTION_TYPES = {
}
TRANSACTION_TYPES = PURCHASE_TRANSACTION_TYPES | SALES_TRANSACTION_TYPES
# Party-derived fields that must NOT be auto-copied by `get_mapped_doc` when the
# source and target documents belong to different parties (e.g. Sales Order →
# Purchase Order or inter-company Sales Invoice → Purchase Invoice).
CROSS_PARTY_FIELD_NO_MAP = [
"tax_category",
"tax_id",
"tax_withholding_category",
"taxes_and_charges",
"address_display",
"contact_display",
"contact_mobile",
"contact_email",
"contact_person",
"shipping_address",
"dispatch_address",
"payment_terms_template",
"language",
]
class DuplicatePartyAccountError(frappe.ValidationError):
pass
@@ -491,11 +510,6 @@ def get_party_advance_account(party_type, party, company):
return account
@frappe.whitelist()
def get_party_bank_account(party_type, party):
return frappe.db.get_value("Bank Account", {"party_type": party_type, "party": party, "is_default": 1})
def get_party_account_currency(party_type, party, company):
def generator():
party_account = get_party_account(party_type, party, company)
@@ -530,11 +544,19 @@ def get_party_gle_currency(party_type, party, company):
def get_party_gle_account(party_type, party, company):
def generator():
existing_gle_account = frappe.db.sql(
"""select account from `tabGL Entry`
where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s
limit 1""",
{"company": company, "party_type": party_type, "party": party},
gl = qb.DocType("GL Entry")
existing_gle_account = (
qb.from_(gl)
.select(gl.account)
.where(
(gl.docstatus == 1)
& (gl.company == company)
& (gl.party_type == party_type)
& (gl.party == party)
& (gl.is_cancelled == 0)
)
.limit(1)
.run()
)
return existing_gle_account[0][0] if existing_gle_account else None
@@ -669,7 +691,7 @@ def validate_due_date_with_template(posting_date, due_date, bill_date, template_
if not default_due_date:
return
if default_due_date != posting_date and getdate(due_date) > getdate(default_due_date):
if getdate(default_due_date) != getdate(posting_date) and getdate(due_date) > getdate(default_due_date):
if frappe.db.get_single_value("Accounts Settings", "credit_controller") in frappe.get_roles():
party_type = "supplier" if doctype == "Purchase Invoice" else "customer"
@@ -743,7 +765,7 @@ def set_taxes(
args.update({"tax_type": "Purchase"})
if use_for_shopping_cart:
args.update({"use_for_shopping_cart": use_for_shopping_cart})
args.update({"use_for_shopping_cart": cint(use_for_shopping_cart)})
return get_tax_template(posting_date, args)

View File

@@ -1,23 +0,0 @@
{
"align_labels_right": 0,
"creation": "2016-05-05 17:16:18.564460",
"custom_format": 1,
"disabled": 0,
"doc_type": "Sales Invoice",
"docstatus": 0,
"doctype": "Print Format",
"font": "Default",
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ company }}<br>\n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}<br>\n</p>\n<p>\n\t<b>{{ __(\"Customer\") }}:</b> {{ customer }}<br>\n</p>\n\n<p>\n\t<b>{{ __(\"Date\") }}:</b> {{ dateutil.global_date_format(posting_date) }}<br>\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ __(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{% for item in items %}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_name }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ format_number(item.qty, null,precision(\"difference\")) }}<br>@ {{ format_currency(item.rate, currency) }}</td>\n\t\t\t<td class=\"text-right\">{{ format_currency(item.amount, currency) }}</td>\n\t\t</tr>\n\t\t{% endfor %}\n\t</tbody>\n</table>\n\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Qty Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ qty_total }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n\n\n<hr>\n<p>{{ terms }}</p>\n<p class=\"text-center\">{{ __(\"Thank you, please visit again.\") }}</p>",
"idx": 0,
"line_breaks": 0,
"modified": "2019-09-05 17:20:30.726659",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Point of Sale",
"owner": "Administrator",
"print_format_builder": 0,
"print_format_type": "JS",
"raw_printing": 0,
"show_section_headings": 0,
"standard": "Yes"
}

View File

@@ -34,6 +34,17 @@ frappe.query_reports["Accounts Payable"] = {
},
options: "Cost Center",
},
{
fieldname: "project",
label: __("Project"),
fieldtype: "MultiSelectList",
options: "Project",
get_data: function (txt) {
return frappe.db.get_link_options("Project", txt, {
company: frappe.query_report.get_filter_value("company"),
});
},
},
{
fieldname: "party_account",
label: __("Payable Account"),

View File

@@ -120,3 +120,49 @@ class TestAccountsPayable(AccountsTestMixin, FrappeTestCase):
self.assertEqual(len(report[1]), 2)
self.assertEqual([pi.name, payment_term1.payment_term_name], [row.voucher_no, row.payment_term])
def test_project_filter(self):
project = frappe.get_doc(
{"doctype": "Project", "project_name": "_Test AP Project", "company": self.company}
).insert()
pi = self.create_purchase_invoice(do_not_submit=True)
pi.project = project.name
pi.save().submit()
filters = {
"company": self.company,
"report_date": today(),
"range": "30, 60, 90, 120",
"project": [project.name],
}
report = execute(filters)[1]
self.assertEqual(len(report), 1)
row = report[0]
self.assertEqual(row.project, project.name)
self.assertEqual(row.invoiced, 300.0)
def test_project_on_report_output(self):
"""
Report row must carry the invoice's project.
"""
filters = {
"company": self.company,
"report_date": today(),
"range": "30, 60, 90, 120",
}
project = frappe.get_doc(
{"doctype": "Project", "project_name": "_Test AP Project Output", "company": self.company}
).insert()
pi = self.create_purchase_invoice(do_not_submit=True)
pi.project = project.name
pi.save().submit()
report = execute(filters)
self.assertEqual(len(report[1]), 1)
row = report[1][0]
self.assertEqual([pi.name, project.name, 300], [row.voucher_no, row.project, row.outstanding])

View File

@@ -53,6 +53,17 @@ frappe.query_reports["Accounts Payable Summary"] = {
},
options: "Cost Center",
},
{
fieldname: "project",
label: __("Project"),
fieldtype: "MultiSelectList",
options: "Project",
get_data: function (txt) {
return frappe.db.get_link_options("Project", txt, {
company: frappe.query_report.get_filter_value("company"),
});
},
},
{
fieldname: "party_type",
label: __("Party Type"),

View File

@@ -36,6 +36,17 @@ frappe.query_reports["Accounts Receivable"] = {
},
options: "Cost Center",
},
{
fieldname: "project",
label: __("Project"),
fieldtype: "MultiSelectList",
options: "Project",
get_data: function (txt) {
return frappe.db.get_link_options("Project", txt, {
company: frappe.query_report.get_filter_value("company"),
});
},
},
{
fieldname: "party_type",
label: __("Party Type"),

View File

@@ -129,8 +129,6 @@ class ReceivablePayableReport:
self.fetch_ple_in_buffered_cursor()
elif self.ple_fetch_method == "UnBuffered Cursor":
self.fetch_ple_in_unbuffered_cursor()
elif self.ple_fetch_method == "Raw SQL":
self.fetch_ple_in_sql_procedures()
# Build delivery note map against all sales invoices
self.build_delivery_note_map()
@@ -194,6 +192,7 @@ class ReceivablePayableReport:
and ple.against_voucher_type in self.advance_payment_doctypes
):
self.voucher_balance[key].cost_center = ple.cost_center
self.voucher_balance[key].project = ple.project
self.get_invoices(ple)
@@ -320,79 +319,6 @@ class ReceivablePayableReport:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
def fetch_ple_in_sql_procedures(self):
self.proc = InitSQLProceduresForAR()
build_balance = f"""
begin not atomic
declare done boolean default false;
declare rec1 row type of `{self.proc._row_def_table_name}`;
declare ple cursor for {self.ple_query.get_sql()};
declare continue handler for not found set done = true;
open ple;
fetch ple into rec1;
while not done do
call {self.proc.init_procedure_name}(rec1);
fetch ple into rec1;
end while;
close ple;
set done = false;
open ple;
fetch ple into rec1;
while not done do
call {self.proc.allocate_procedure_name}(rec1);
fetch ple into rec1;
end while;
close ple;
end;
"""
frappe.db.sql(build_balance)
balances = frappe.db.sql(
f"""select
name,
voucher_type,
voucher_no,
party,
party_account `account`,
posting_date,
account_currency,
cost_center,
sum(invoiced) `invoiced`,
sum(paid) `paid`,
sum(credit_note) `credit_note`,
sum(invoiced) - sum(paid) - sum(credit_note) `outstanding`,
sum(invoiced_in_account_currency) `invoiced_in_account_currency`,
sum(paid_in_account_currency) `paid_in_account_currency`,
sum(credit_note_in_account_currency) `credit_note_in_account_currency`,
sum(invoiced_in_account_currency) - sum(paid_in_account_currency) - sum(credit_note_in_account_currency) `outstanding_in_account_currency`
from `{self.proc._voucher_balance_name}` group by name order by posting_date;""",
as_dict=True,
)
for x in balances:
if self.filters.get("ignore_accounts"):
key = (x.voucher_type, x.voucher_no, x.party)
else:
key = (x.account, x.voucher_type, x.voucher_no, x.party)
_d = self.build_voucher_dict(x)
for field in [
"invoiced",
"paid",
"credit_note",
"outstanding",
"invoiced_in_account_currency",
"paid_in_account_currency",
"credit_note_in_account_currency",
"outstanding_in_account_currency",
"cost_center",
]:
_d[field] = x.get(field)
self.voucher_balance[key] = _d
def update_sub_total_row(self, row, party):
total_row = self.total_row_map.get(party)
@@ -925,6 +851,7 @@ class ReceivablePayableReport:
ple.against_voucher_no,
ple.party_type,
ple.cost_center,
ple.project,
ple.party,
ple.posting_date,
ple.due_date,
@@ -992,8 +919,31 @@ class ReceivablePayableReport:
if self.filters.cost_center:
self.get_cost_center_conditions()
if self.filters.project:
self.qb_selection_filter.append(self.ple.project.isin(self.filters.project))
self.add_user_permission_filters()
self.add_accounting_dimensions_filters()
def add_user_permission_filters(self):
# Party is a dynamic link, so match conditions cannot auto-apply Customer/Supplier user permissions
from frappe.core.doctype.user_permission.user_permission import get_user_permissions
from frappe.permissions import get_allowed_docs_for_doctype
user_permissions = get_user_permissions()
if not user_permissions:
return
for party_type in self.party_type:
if party_type not in user_permissions:
continue
allowed_parties = get_allowed_docs_for_doctype(user_permissions[party_type], party_type)
self.qb_selection_filter.append(
(self.ple.party_type != party_type) | self.ple.party.isin(allowed_parties or [""])
)
def get_cost_center_conditions(self):
cost_center_list = get_cost_centers_with_children(self.filters.cost_center)
self.qb_selection_filter.append(self.ple.cost_center.isin(cost_center_list))
@@ -1231,6 +1181,7 @@ class ReceivablePayableReport:
)
self.add_column(label=_("Cost Center"), fieldname="cost_center", fieldtype="Data")
self.add_column(label=_("Project"), fieldname="project", fieldtype="Link", options="Project")
self.add_column(label=_("Voucher Type"), fieldname="voucher_type", fieldtype="Data")
self.add_column(
label=_("Voucher No"),
@@ -1382,134 +1333,3 @@ def get_party_group_with_children(party, party_groups):
frappe.throw(_("{0}: {1} does not exist").format(group_dtype, d))
return list(set(all_party_groups))
class InitSQLProceduresForAR:
"""
Initialize SQL Procedures, Functions and Temporary tables to build Receivable / Payable report
"""
_varchar_type = get_definition("Data")
_currency_type = get_definition("Currency")
# Temporary Tables
_voucher_balance_name = "_ar_voucher_balance"
_voucher_balance_definition = f"""
create temporary table `{_voucher_balance_name}`(
name {_varchar_type},
voucher_type {_varchar_type},
voucher_no {_varchar_type},
party {_varchar_type},
party_account {_varchar_type},
posting_date date,
account_currency {_varchar_type},
cost_center {_varchar_type},
invoiced {_currency_type},
paid {_currency_type},
credit_note {_currency_type},
invoiced_in_account_currency {_currency_type},
paid_in_account_currency {_currency_type},
credit_note_in_account_currency {_currency_type}) engine=memory;
"""
_row_def_table_name = "_ar_ple_row"
_row_def_table_definition = f"""
create temporary table `{_row_def_table_name}`(
name {_varchar_type},
account {_varchar_type},
voucher_type {_varchar_type},
voucher_no {_varchar_type},
against_voucher_type {_varchar_type},
against_voucher_no {_varchar_type},
party_type {_varchar_type},
cost_center {_varchar_type},
party {_varchar_type},
posting_date date,
due_date date,
account_currency {_varchar_type},
amount {_currency_type},
amount_in_account_currency {_currency_type}) engine=memory;
"""
# Function
genkey_function_name = "ar_genkey"
genkey_function_sql = f"""
create function `{genkey_function_name}`(rec row type of `{_row_def_table_name}`, allocate bool) returns char(40)
begin
if allocate then
return sha1(concat_ws(',', rec.account, rec.against_voucher_type, rec.against_voucher_no, rec.party));
else
return sha1(concat_ws(',', rec.account, rec.voucher_type, rec.voucher_no, rec.party));
end if;
end
"""
# Procedures
init_procedure_name = "ar_init_tmp_table"
init_procedure_sql = f"""
create procedure ar_init_tmp_table(in ple row type of `{_row_def_table_name}`)
begin
if not exists (select name from `{_voucher_balance_name}` where name = `{genkey_function_name}`(ple, false))
then
insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, false), ple.voucher_type, ple.voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency, ple.cost_center, 0, 0, 0, 0, 0, 0);
end if;
end;
"""
allocate_procedure_name = "ar_allocate_to_tmp_table"
allocate_procedure_sql = f"""
create procedure ar_allocate_to_tmp_table(in ple row type of `{_row_def_table_name}`)
begin
declare invoiced {_currency_type} default 0;
declare invoiced_in_account_currency {_currency_type} default 0;
declare paid {_currency_type} default 0;
declare paid_in_account_currency {_currency_type} default 0;
declare credit_note {_currency_type} default 0;
declare credit_note_in_account_currency {_currency_type} default 0;
if ple.amount > 0 then
if (ple.voucher_type in ("Journal Entry", "Payment Entry") and (ple.voucher_no != ple.against_voucher_no)) then
set paid = -1 * ple.amount;
set paid_in_account_currency = -1 * ple.amount_in_account_currency;
else
set invoiced = ple.amount;
set invoiced_in_account_currency = ple.amount_in_account_currency;
end if;
else
if ple.voucher_type in ("Sales Invoice", "Purchase Invoice") then
if (ple.voucher_no = ple.against_voucher_no) then
set paid = -1 * ple.amount;
set paid_in_account_currency = -1 * ple.amount_in_account_currency;
else
set credit_note = -1 * ple.amount;
set credit_note_in_account_currency = -1 * ple.amount_in_account_currency;
end if;
else
set paid = -1 * ple.amount;
set paid_in_account_currency = -1 * ple.amount_in_account_currency;
end if;
end if;
insert into `{_voucher_balance_name}` values (`{genkey_function_name}`(ple, true), ple.against_voucher_type, ple.against_voucher_no, ple.party, ple.account, ple.posting_date, ple.account_currency,'', invoiced, paid, 0, invoiced_in_account_currency, paid_in_account_currency, 0);
end;
"""
def __init__(self):
existing_procedures = frappe.db.get_routines()
if self.genkey_function_name not in existing_procedures:
frappe.db.sql(self.genkey_function_sql)
if self.init_procedure_name not in existing_procedures:
frappe.db.sql(self.init_procedure_sql)
if self.allocate_procedure_name not in existing_procedures:
frappe.db.sql(self.allocate_procedure_sql)
frappe.db.sql(f"drop table if exists `{self._voucher_balance_name}`")
frappe.db.sql(self._voucher_balance_definition)
frappe.db.sql(f"drop table if exists `{self._row_def_table_name}`")
frappe.db.sql(self._row_def_table_definition)

View File

@@ -779,6 +779,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
"customer_name": "Jane Doe",
"type": "Individual",
"default_currency": "USD",
"customer_group": "Individual",
}
)
.insert()
@@ -1002,6 +1003,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
"customer_name": "Jane Doe",
"type": "Individual",
"default_currency": "USD",
"customer_group": "Individual",
}
)
.insert()
@@ -1202,3 +1204,102 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
self.assertEqual(len(report[1]), 2)
self.assertEqual([si.name, payment_term1.payment_term_name], [row.voucher_no, row.payment_term])
def test_project_filter(self):
project = frappe.get_doc(
{"doctype": "Project", "project_name": "_Test AR Project", "company": self.company}
).insert()
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
si.project = project.name
si.save().submit()
filters = {
"company": self.company,
"report_date": today(),
"range": "30, 60, 90, 120",
"project": [project.name],
}
report = execute(filters)[1]
self.assertEqual(len(report), 1)
row = report[0]
self.assertEqual(row.project, project.name)
self.assertEqual(row.invoiced, 100.0)
def test_project_on_report_output(self):
"""
Report row must carry the invoice's project even when the payment entry
has no project set.
"""
filters = {
"company": self.company,
"report_date": today(),
"range": "30, 60, 90, 120",
}
project = frappe.get_doc(
{"doctype": "Project", "project_name": "_Test AR Project Output", "company": self.company}
).insert()
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
si.project = project.name
si.save().submit()
# payment has no project — report row must still show the invoice's project
self.create_payment_entry(si.name)
report = execute(filters)
self.assertEqual(len(report[1]), 1)
row = report[1][0]
self.assertEqual([si.name, project.name, 60], [row.voucher_no, row.project, row.outstanding])
def test_accounts_receivable_respects_user_permissions(self):
# Party is a dynamic link on Payment Ledger Entry, so user permissions on Customer
# must be applied explicitly. The report should only show permitted customers.
# Running the report writes an access log that commits, so these invoices survive
# tearDown's rollback. Delete and commit them so they don't leak into other tests.
def remove_committed_entries():
self.clear_old_entries()
frappe.db.commit() # nosemgrep
self.addCleanup(remove_committed_entries)
original_customer = self.customer
second_customer = "_Test AR Perm Customer"
# create_customer overrides self.customer, so build the restricted invoice first
self.create_customer(customer_name=second_customer)
self.create_sales_invoice(no_payment_schedule=True)
self.customer = original_customer
allowed_invoice = self.create_sales_invoice(no_payment_schedule=True)
test_user = "test_ar_user_permission@example.com"
if not frappe.db.exists("User", test_user):
user = frappe.new_doc("User")
user.email = test_user
user.first_name = "AR Perm"
user.append("roles", {"role": "Accounts User"})
user.save()
frappe.permissions.add_user_permission("Customer", original_customer, test_user)
filters = {
"company": self.company,
"party_type": "Customer",
"report_date": today(),
"range": "30, 60, 90, 120",
}
frappe.set_user(test_user)
try:
report = execute(filters)
finally:
frappe.set_user("Administrator")
parties = {row.party for row in report[1]}
self.assertIn(original_customer, parties)
self.assertNotIn(second_customer, parties)
self.assertEqual(allowed_invoice.customer, original_customer)

View File

@@ -53,6 +53,17 @@ frappe.query_reports["Accounts Receivable Summary"] = {
},
options: "Cost Center",
},
{
fieldname: "project",
label: __("Project"),
fieldtype: "MultiSelectList",
options: "Project",
get_data: function (txt) {
return frappe.db.get_link_options("Project", txt, {
company: frappe.query_report.get_filter_value("company"),
});
},
},
{
fieldname: "party_type",
label: __("Party Type"),

View File

@@ -4,7 +4,10 @@
import frappe
from frappe import _
from frappe.utils import getdate, nowdate
from frappe.query_builder import Case
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import getdate
from pypika import Order
def execute(filters=None):
@@ -48,17 +51,6 @@ def get_columns():
return columns
def get_conditions(filters):
conditions = ""
if filters.get("from_date"):
conditions += " and posting_date>=%(from_date)s"
if filters.get("to_date"):
conditions += " and posting_date<=%(to_date)s"
return conditions
def get_entries(filters):
entries = []
@@ -73,41 +65,90 @@ def get_entries(filters):
return sorted(
entries,
key=lambda k: k[2].strftime("%H%M%S") or getdate(nowdate()),
key=lambda k: getdate(k[2]),
)
def get_entries_for_bank_clearance_summary(filters):
entries = []
conditions = get_conditions(filters)
je = frappe.qb.DocType("Journal Entry")
jea = frappe.qb.DocType("Journal Entry Account")
journal_entries = frappe.db.sql(
f"""SELECT
"Journal Entry", jv.name, jv.posting_date, jv.cheque_no,
jv.clearance_date, jvd.against_account, jvd.debit - jvd.credit
FROM
`tabJournal Entry Account` jvd, `tabJournal Entry` jv
WHERE
jvd.parent = jv.name and jv.docstatus=1 and jvd.account = %(account)s {conditions}
order by posting_date DESC, jv.name DESC""",
filters,
as_list=1,
)
journal_entries = (
frappe.qb.from_(jea)
.inner_join(je)
.on(jea.parent == je.name)
.select(
ConstantColumn("Journal Entry").as_("payment_document"),
je.name.as_("payment_entry"),
je.posting_date,
je.cheque_no,
je.clearance_date,
jea.against_account,
jea.debit_in_account_currency - jea.credit_in_account_currency,
)
.where(
(jea.account == filters.account)
& (je.docstatus == 1)
& (je.posting_date >= filters.from_date)
& (je.posting_date <= filters.to_date)
& ((je.is_opening == "No") | (je.is_opening.isnull()))
)
.orderby(je.posting_date, order=Order.desc)
.orderby(je.name, order=Order.desc)
).run(as_list=True)
payment_entries = frappe.db.sql(
f"""SELECT
"Payment Entry", name, posting_date, reference_no, clearance_date, party,
if(paid_from=%(account)s, ((paid_amount * -1) - total_taxes_and_charges) , received_amount)
FROM
`tabPayment Entry`
WHERE
docstatus=1 and (paid_from = %(account)s or paid_to = %(account)s) {conditions}
order by posting_date DESC, name DESC""",
filters,
as_list=1,
)
pe = frappe.qb.DocType("Payment Entry")
payment_entries = (
frappe.qb.from_(pe)
.select(
ConstantColumn("Payment Entry").as_("payment_document"),
pe.name.as_("payment_entry"),
pe.posting_date,
pe.reference_no.as_("cheque_no"),
pe.clearance_date,
pe.party.as_("against_account"),
Case()
.when(
(pe.paid_from == filters.account),
((pe.paid_amount * -1) - pe.total_taxes_and_charges),
)
.else_(pe.received_amount),
)
.where((pe.paid_from == filters.account) | (pe.paid_to == filters.account))
.where(
(pe.docstatus == 1)
& (pe.posting_date >= filters.from_date)
& (pe.posting_date <= filters.to_date)
)
.orderby(pe.posting_date, order=Order.desc)
.orderby(pe.name, order=Order.desc)
).run(as_list=True)
entries = journal_entries + payment_entries
pi = frappe.qb.DocType("Purchase Invoice")
purchase_invoices = (
frappe.qb.from_(pi)
.select(
ConstantColumn("Purchase Invoice").as_("payment_document"),
pi.name.as_("payment_entry"),
pi.posting_date,
pi.bill_no.as_("cheque_no"),
pi.clearance_date,
pi.supplier.as_("against_account"),
(pi.paid_amount * -1).as_("amount"),
)
.where(
(pi.docstatus == 1)
& (pi.is_paid == 1)
& (pi.cash_bank_account == filters.account)
& (pi.posting_date >= filters.from_date)
& (pi.posting_date <= filters.to_date)
)
.orderby(pi.posting_date, order=Order.desc)
.orderby(pi.name, order=Order.desc)
).run(as_list=True)
entries = journal_entries + payment_entries + purchase_invoices
return entries

View File

@@ -4,7 +4,11 @@
import frappe
from frappe import _
from frappe.query_builder import Case
from frappe.query_builder.custom import ConstantColumn
from frappe.query_builder.functions import Coalesce, Sum
from frappe.utils import flt, getdate
from pypika import Order
from erpnext.accounts.utils import get_balance_on
@@ -123,73 +127,143 @@ def get_entries_for_bank_reconciliation_statement(filters):
payment_entries = get_payment_entries(filters)
purchase_invoices = get_purchase_invoices(filters)
pos_entries = []
if filters.include_pos_transactions:
pos_entries = get_pos_entries(filters)
return list(journal_entries) + list(payment_entries) + list(pos_entries)
return list(journal_entries) + list(payment_entries) + list(pos_entries) + list(purchase_invoices)
def get_journal_entries(filters):
return frappe.db.sql(
"""
select "Journal Entry" as payment_document, jv.posting_date,
jv.name as payment_entry, jvd.debit_in_account_currency as debit,
jvd.credit_in_account_currency as credit, jvd.against_account,
jv.cheque_no as reference_no, jv.cheque_date as ref_date, jv.clearance_date, jvd.account_currency
from
`tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
and ifnull(jv.is_opening, 'No') = 'No'
and jv.company = %(company)s """,
filters,
as_dict=1,
)
je = frappe.qb.DocType("Journal Entry")
jea = frappe.qb.DocType("Journal Entry Account")
return (
frappe.qb.from_(jea)
.join(je)
.on(jea.parent == je.name)
.select(
ConstantColumn("Journal Entry").as_("payment_document"),
je.name.as_("payment_entry"),
je.posting_date,
jea.debit_in_account_currency.as_("debit"),
jea.credit_in_account_currency.as_("credit"),
jea.against_account,
je.cheque_no.as_("reference_no"),
je.cheque_date.as_("ref_date"),
je.clearance_date,
jea.account_currency,
)
.where(
(je.docstatus == 1)
& (jea.account == filters.account)
& (je.posting_date <= filters.report_date)
& (je.clearance_date.isnull() | (je.clearance_date > filters.report_date))
& (je.company == filters.company)
& ((je.is_opening.isnull()) | (je.is_opening == "No"))
)
.orderby(je.posting_date)
.orderby(je.name, order=Order.desc)
).run(as_dict=True)
def get_payment_entries(filters):
return frappe.db.sql(
"""
select
"Payment Entry" as payment_document, name as payment_entry,
reference_no, reference_date as ref_date,
if(paid_to=%(account)s, received_amount_after_tax, 0) as debit,
if(paid_from=%(account)s, paid_amount_after_tax, 0) as credit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
from `tabPayment Entry`
where
(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
and posting_date <= %(report_date)s
and ifnull(clearance_date, '4000-01-01') > %(report_date)s
and company = %(company)s
""",
filters,
as_dict=1,
)
pe = frappe.qb.DocType("Payment Entry")
return (
frappe.qb.from_(pe)
.select(
ConstantColumn("Payment Entry").as_("payment_document"),
pe.name.as_("payment_entry"),
pe.reference_no.as_("reference_no"),
pe.reference_date.as_("ref_date"),
Case().when(pe.paid_to == filters.account, pe.received_amount_after_tax).else_(0).as_("debit"),
Case().when(pe.paid_from == filters.account, pe.paid_amount_after_tax).else_(0).as_("credit"),
pe.posting_date,
Coalesce(
pe.party, Case().when(pe.paid_from == filters.account, pe.paid_to).else_(pe.paid_from)
).as_("against_account"),
pe.clearance_date,
(
Case()
.when(pe.paid_to == filters.account, pe.paid_to_account_currency)
.else_(pe.paid_from_account_currency)
).as_("account_currency"),
)
.where(
(pe.docstatus == 1)
& ((pe.paid_from == filters.account) | (pe.paid_to == filters.account))
& (pe.posting_date <= filters.report_date)
& (pe.clearance_date.isnull() | (pe.clearance_date > filters.report_date))
& (pe.company == filters.company)
)
.orderby(pe.posting_date)
.orderby(pe.name, order=Order.desc)
).run(as_dict=True)
def get_purchase_invoices(filters):
pi = frappe.qb.DocType("Purchase Invoice")
acc = frappe.qb.DocType("Account")
return (
frappe.qb.from_(pi)
.inner_join(acc)
.on(pi.cash_bank_account == acc.name)
.select(
ConstantColumn("Purchase Invoice").as_("payment_document"),
pi.name.as_("payment_entry"),
pi.bill_no.as_("reference_no"),
pi.posting_date.as_("ref_date"),
Case().when(pi.paid_amount < 0, pi.paid_amount * -1).else_(0).as_("debit"),
Case().when(pi.paid_amount > 0, pi.paid_amount).else_(0).as_("credit"),
pi.posting_date,
pi.supplier.as_("against_account"),
pi.clearance_date,
acc.account_currency,
)
.where(
(pi.docstatus == 1)
& (pi.is_paid == 1)
& (pi.cash_bank_account == filters.account)
& (pi.posting_date <= filters.report_date)
& (pi.clearance_date.isnull() | (pi.clearance_date > filters.report_date))
& (pi.company == filters.company)
)
.orderby(pi.posting_date)
.orderby(pi.name, order=Order.desc)
).run(as_dict=True)
def get_pos_entries(filters):
return frappe.db.sql(
"""
select
"Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
si.posting_date, si.debit_to as against_account, sip.clearance_date,
account.account_currency, 0 as credit
from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account
where
sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
and account.name = sip.account and si.posting_date <= %(report_date)s and
ifnull(sip.clearance_date, '4000-01-01') > %(report_date)s
and si.company = %(company)s
order by
si.posting_date ASC, si.name DESC
""",
filters,
as_dict=1,
)
si = frappe.qb.DocType("Sales Invoice")
si_payment = frappe.qb.DocType("Sales Invoice Payment")
acc = frappe.qb.DocType("Account")
return (
frappe.qb.from_(si_payment)
.join(si)
.on(si_payment.parent == si.name)
.join(acc)
.on(si_payment.account == acc.name)
.select(
ConstantColumn("Sales Invoice").as_("payment_document"),
si.name.as_("payment_entry"),
si_payment.amount.as_("debit"),
si.posting_date,
si.debit_to.as_("against_account"),
si_payment.clearance_date,
acc.account_currency,
ConstantColumn(0).as_("credit"),
)
.where(
(si_payment.account == filters.account)
& (si.docstatus == 1)
& (si.posting_date <= filters.report_date)
& (si_payment.clearance_date.isnull() | (si_payment.clearance_date > filters.report_date))
& (si.company == filters.company)
)
.orderby(si.posting_date)
.orderby(si_payment.name, order=Order.desc)
).run(as_dict=True)
def get_amounts_not_reflected_in_system(filters):
@@ -205,30 +279,66 @@ def get_amounts_not_reflected_in_system(filters):
def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters):
je_amount = frappe.db.sql(
"""
select sum(jvd.debit_in_account_currency - jvd.credit_in_account_currency)
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%(account)s
and jv.posting_date > %(report_date)s and jv.clearance_date <= %(report_date)s
and ifnull(jv.is_opening, 'No') = 'No' """,
filters,
je = frappe.qb.DocType("Journal Entry")
jea = frappe.qb.DocType("Journal Entry Account")
je_amount = (
frappe.qb.from_(jea)
.inner_join(je)
.on(jea.parent == je.name)
.select(
Sum(jea.debit_in_account_currency - jea.credit_in_account_currency).as_("amount"),
)
.where(
(je.docstatus == 1)
& (jea.account == filters.account)
& (je.posting_date > filters.report_date)
& (je.clearance_date <= filters.report_date)
& (je.company == filters.company)
& ((je.is_opening.isnull()) | (je.is_opening == "No"))
)
.run(as_dict=True)
)
je_amount = flt(je_amount[0].amount) if je_amount else 0.0
je_amount = flt(je_amount[0][0]) if je_amount else 0.0
pe_amount = frappe.db.sql(
"""
select sum(if(paid_from=%(account)s, paid_amount, received_amount))
from `tabPayment Entry`
where (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
and posting_date > %(report_date)s and clearance_date <= %(report_date)s""",
filters,
pe = frappe.qb.DocType("Payment Entry")
pe_amount = (
frappe.qb.from_(pe)
.select(
Sum(Case().when(pe.paid_from == filters.account, pe.paid_amount).else_(pe.received_amount)).as_(
"amount"
),
)
.where(
((pe.paid_from == filters.account) | (pe.paid_to == filters.account))
& (pe.docstatus == 1)
& (pe.posting_date > filters.report_date)
& (pe.clearance_date <= filters.report_date)
& (pe.company == filters.company)
)
.run(as_dict=True)
)
pe_amount = flt(pe_amount[0].amount) if pe_amount else 0.0
pe_amount = flt(pe_amount[0][0]) if pe_amount else 0.0
pi = frappe.qb.DocType("Purchase Invoice")
pi_amount = (
frappe.qb.from_(pi)
.select(
Sum(pi.paid_amount).as_("amount"),
)
.where(
(pi.docstatus == 1)
& (pi.is_paid == 1)
& (pi.cash_bank_account == filters.account)
& (pi.posting_date > filters.report_date)
& (pi.clearance_date <= filters.report_date)
& (pi.company == filters.company)
)
).run(as_dict=True)
return je_amount + pe_amount
pi_amount = flt(pi_amount[0].amount) if pi_amount else 0.0
return je_amount + pe_amount + pi_amount
def get_balance_row(label, amount, account_currency):

View File

@@ -8,6 +8,7 @@ import frappe
from frappe import _
from frappe.utils import flt, formatdate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
@@ -15,6 +16,8 @@ def execute(filters=None):
if not filters:
filters = {}
validate_filters(filters)
columns = get_columns(filters)
if filters.get("budget_against_filter"):
dimensions = filters.get("budget_against_filter")
@@ -35,6 +38,21 @@ def execute(filters=None):
return columns, data, None, chart
def validate_filters(filters):
validate_budget_dimensions(filters)
def validate_budget_dimensions(filters):
dimensions = [d.get("document_type") for d in get_dimensions(with_cost_center_and_project=True)[0]]
if filters.get("budget_against") and filters.get("budget_against") not in dimensions:
frappe.throw(
title=_("Invalid Accounting Dimension"),
msg=_("{0} is not a valid Accounting Dimension.").format(
frappe.bold(filters.get("budget_against"))
),
)
def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation):
for account, monthwise_data in dimension_items.items():
row = [dimension, account]

View File

@@ -48,6 +48,9 @@ class Deferred_Item:
Generate report data for output
"""
ret_data = frappe._dict({"name": self.item_name})
ret_data.service_start_date = self.service_start_date
ret_data.service_end_date = self.service_end_date
ret_data.amount = self.base_net_amount
for period in self.period_total:
ret_data[period.key] = period.total
ret_data.indent = 1
@@ -205,6 +208,9 @@ class Deferred_Invoice:
for item in self.uniq_items:
self.items.append(Deferred_Item(item, self, [x for x in items if x.item == item]))
# roll-up amount from all deferred items
self.amount_total = sum(item.base_net_amount for item in self.items)
def calculate_invoice_revenue_expense_for_period(self):
"""
calculate deferred revenue/expense for all items in invoice
@@ -232,7 +238,7 @@ class Deferred_Invoice:
generate report data for invoice, includes invoice total
"""
ret_data = []
inv_total = frappe._dict({"name": self.name})
inv_total = frappe._dict({"name": self.name, "amount": self.amount_total})
for x in self.period_total:
inv_total[x.key] = x.total
inv_total.indent = 0
@@ -386,6 +392,24 @@ class Deferred_Revenue_and_Expense_Report:
def get_columns(self):
columns = []
columns.append({"label": _("Name"), "fieldname": "name", "fieldtype": "Data", "read_only": 1})
columns.append(
{
"label": _("Service Start Date"),
"fieldname": "service_start_date",
"fieldtype": "Date",
"read_only": 1,
}
)
columns.append(
{
"label": _("Service End Date"),
"fieldname": "service_end_date",
"fieldtype": "Date",
"read_only": 1,
}
)
columns.append({"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "read_only": 1})
for period in self.period_list:
columns.append(
{
@@ -415,6 +439,8 @@ class Deferred_Revenue_and_Expense_Report:
elif self.filters.type == "Expense":
total_row = frappe._dict({"name": "Total Deferred Expense"})
total_row["amount"] = sum(inv.amount_total for inv in self.deferred_invoices)
for idx, period in enumerate(self.period_list, 0):
total_row[period.key] = self.period_total[idx].total
ret.append(total_row)

View File

@@ -37,6 +37,20 @@ function get_filters() {
});
},
},
{
fieldname: "party_type",
label: __("Party Type"),
fieldtype: "Link",
options: "Party Type",
width: 100,
},
{
fieldname: "party",
label: __("Party"),
fieldtype: "Dynamic Link",
options: "party_type",
width: 100,
},
{
fieldname: "voucher_no",
label: __("Voucher No"),

View File

@@ -68,6 +68,12 @@ class General_Payment_Ledger_Comparison:
if self.filters.period_end_date:
filter_criterion.append(gle.posting_date.lte(self.filters.period_end_date))
if self.filters.party_type:
filter_criterion.append(gle.party_type.eq(self.filters.party_type))
if self.filters.party:
filter_criterion.append(gle.party.eq(self.filters.party))
if acc_type == "receivable":
outstanding = (Sum(gle.debit) - Sum(gle.credit)).as_("outstanding")
else:
@@ -111,6 +117,12 @@ class General_Payment_Ledger_Comparison:
if self.filters.period_end_date:
filter_criterion.append(ple.posting_date.lte(self.filters.period_end_date))
if self.filters.party_type:
filter_criterion.append(ple.party_type.eq(self.filters.party_type))
if self.filters.party:
filter_criterion.append(ple.party.eq(self.filters.party))
self.account_types[acc_type].ple = (
qb.from_(ple)
.select(

View File

@@ -176,10 +176,16 @@ frappe.query_reports["General Ledger"] = {
fieldtype: "Check",
default: 1,
},
{
fieldname: "disable_opening_balance_calculation",
label: __("Disable Opening Balance Calculation"),
fieldtype: "Check",
},
{
fieldname: "show_opening_entries",
label: __("Show Opening Entries"),
fieldtype: "Check",
depends_on: "eval: !doc.disable_opening_balance_calculation",
},
{
fieldname: "include_default_book_entries",

View File

@@ -279,7 +279,15 @@ def get_conditions(filters):
if filters.get("party"):
conditions.append("party in %(party)s")
if not (
if filters.get("disable_opening_balance_calculation"):
if not ignore_is_opening:
conditions.append("(posting_date >=%(from_date)s or is_opening = 'Yes')")
else:
conditions.append("posting_date >=%(from_date)s")
# opening balance calculation is done only if filtered on account/party
# so from_date filter is not applied
elif not (
filters.get("account")
or filters.get("party")
or filters.get("categorize_by") in ["Categorize by Account", "Categorize by Party"]
@@ -398,7 +406,13 @@ def get_data_with_opening_closing(filters, account_details, accounting_dimension
# Opening for filtered account
data.append(totals.opening)
if filters.get("categorize_by") != "Categorize by Voucher (Consolidated)":
if not filters.get("categorize_by"):
all_entries = []
for acc_dict in gle_map.values():
all_entries.extend(acc_dict.entries)
data += all_entries
elif filters.get("categorize_by") != "Categorize by Voucher (Consolidated)":
for _acc, acc_dict in gle_map.items():
# acc
if acc_dict.entries:
@@ -528,7 +542,11 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, tot
group_by_value = gle.get(group_by)
gle.voucher_type = gle.voucher_type
if gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries):
if gle.posting_date < from_date or (
cstr(gle.is_opening) == "Yes"
and not show_opening_entries
and not filters.disable_opening_balance_calculation
):
if not group_by_voucher_consolidated:
update_value_in_dict(gle_map[group_by_value].totals, "opening", gle, True)
update_value_in_dict(gle_map[group_by_value].totals, "closing", gle, True)

View File

@@ -649,7 +649,7 @@ class GrossProfitGenerator:
new_row = row
self.set_average_based_on_payment_term_portion(new_row, row, invoice_portion)
else:
new_row.qty += flt(row.qty)
new_row.qty = flt((new_row.qty + row.qty), self.float_precision)
self.set_average_based_on_payment_term_portion(new_row, row, invoice_portion, True)
new_row = self.set_average_rate(new_row)
@@ -659,11 +659,17 @@ class GrossProfitGenerator:
if i == 0:
new_row = row
else:
new_row.qty += flt(row.qty)
new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
new_row.base_amount += flt(row.base_amount, self.currency_precision)
new_row.qty = flt((new_row.qty + row.qty), self.float_precision)
new_row.buying_amount = flt(
(new_row.buying_amount + row.buying_amount), self.currency_precision
)
new_row.base_amount = flt(
(new_row.base_amount + row.base_amount), self.currency_precision
)
if self.filters.get("group_by") == "Sales Person":
new_row.allocated_amount += flt(row.allocated_amount, self.currency_precision)
new_row.allocated_amount = flt(
(new_row.allocated_amount + row.allocated_amount), self.currency_precision
)
new_row = self.set_average_rate(new_row)
self.grouped_data.append(new_row)
@@ -780,19 +786,11 @@ class GrossProfitGenerator:
return self.calculate_buying_amount_from_sle(
row, my_sle, parenttype, parent, row.item_row, item_code
)
elif self.delivery_notes.get((row.parent, row.item_code), None):
# check if Invoice has delivery notes
dn = self.delivery_notes.get((row.parent, row.item_code))
parenttype, parent, item_row, dn_warehouse = (
"Delivery Note",
dn["delivery_note"],
dn["item_row"],
dn["warehouse"],
)
my_sle = self.get_stock_ledger_entries(item_code, dn_warehouse)
return self.calculate_buying_amount_from_sle(
row, my_sle, parenttype, parent, item_row, item_code
)
elif row.item_row and self.delivery_notes.get(row.item_row):
dn = self.delivery_notes[row.item_row]
if flt(dn.total_qty):
return flt(row.qty) * flt(dn.total_incoming_value) / flt(dn.total_qty)
return flt(row.qty) * self.get_average_buying_rate(row, item_code)
elif row.sales_order and row.so_detail:
incoming_amount = self.get_buying_amount_from_so_dn(row.sales_order, row.so_detail, item_code)
if incoming_amount:
@@ -1043,25 +1041,29 @@ class GrossProfitGenerator:
def get_delivery_notes(self):
self.delivery_notes = frappe._dict({})
if self.si_list:
from frappe.query_builder.functions import Sum
invoices = [x.parent for x in self.si_list]
dni = qb.DocType("Delivery Note Item")
delivery_notes = (
qb.from_(dni)
.select(
dni.against_sales_invoice.as_("sales_invoice"),
dni.item_code,
dni.warehouse,
dni.parent.as_("delivery_note"),
dni.name.as_("item_row"),
dni.si_detail,
Sum(dni.stock_qty * dni.incoming_rate).as_("total_incoming_value"),
Sum(dni.stock_qty).as_("total_qty"),
)
.where((dni.docstatus == 1) & (dni.against_sales_invoice.isin(invoices)))
.groupby(dni.against_sales_invoice, dni.item_code)
.orderby(dni.creation, order=Order.desc)
.where(
(dni.docstatus == 1)
& (dni.against_sales_invoice.isin(invoices))
& (dni.si_detail.isnotnull())
& (dni.si_detail != "")
)
.groupby(dni.si_detail)
.run(as_dict=True)
)
for entry in delivery_notes:
self.delivery_notes[(entry.sales_invoice, entry.item_code)] = entry
self.delivery_notes[entry.si_detail] = entry
def group_items_by_invoice(self):
"""

View File

@@ -82,6 +82,7 @@ class TestGrossProfit(FrappeTestCase):
customer = frappe.new_doc("Customer")
customer.customer_name = name
customer.type = "Individual"
customer.customer_group = "Individual"
customer.save()
self.customer = customer.name

View File

@@ -4,6 +4,7 @@
import frappe
from frappe import _
from frappe.query_builder import CustomFunction
from frappe.utils import cint
@@ -94,19 +95,35 @@ def get_data(filters):
def get_sales_details(filters):
item_details_map = {}
date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date"
if filters["based_on"] not in ("Sales Order", "Sales Invoice"):
frappe.throw(_("Invalid value {0} for 'Based On'").format(filters["based_on"]))
sales_data = frappe.db.sql(
"""
select s.territory, s.customer, si.item_group, si.item_code, si.qty, {date_field} as last_order_date,
DATEDIFF(CURRENT_DATE, {date_field}) as days_since_last_order
from `tab{doctype}` s, `tab{doctype} Item` si
where s.name = si.parent and s.docstatus = 1
order by days_since_last_order """.format( # nosec
date_field=date_field, doctype=filters["based_on"]
),
as_dict=1,
)
parent = frappe.qb.DocType(filters["based_on"])
child_doctype = "Sales Order Item" if filters["based_on"] == "Sales Order" else "Sales Invoice Item"
child = frappe.qb.DocType(child_doctype)
date_diff = CustomFunction("DATEDIFF", ["d1", "d2"])
current_date = CustomFunction("CURRENT_DATE", [])
date_col = parent.transaction_date if filters["based_on"] == "Sales Order" else parent.posting_date
days_since_last_order = date_diff(current_date(), date_col)
sales_data = (
frappe.qb.from_(parent)
.inner_join(child)
.on(parent.name == child.parent)
.select(
parent.territory,
parent.customer,
child.item_group,
child.item_code,
child.qty,
date_col.as_("last_order_date"),
days_since_last_order.as_("days_since_last_order"),
)
.where(parent.docstatus == 1)
.orderby(days_since_last_order)
).run(as_dict=True)
for d in sales_data:
item_details_map.setdefault((d.territory, d.item_code), d)

View File

@@ -31,6 +31,7 @@ def _execute(filters=None, additional_table_columns=None):
item_list = get_items(filters, additional_table_columns)
aii_account_map = get_aii_accounts()
default_taxes = {}
if item_list:
itemised_tax, tax_columns = get_tax_accounts(
item_list,
@@ -39,6 +40,9 @@ def _execute(filters=None, additional_table_columns=None):
doctype="Purchase Invoice",
tax_doctype="Purchase Taxes and Charges",
)
for tax in tax_columns:
default_taxes[f"{tax}_rate"] = 0
default_taxes[f"{tax}_amount"] = 0
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
@@ -85,6 +89,8 @@ def _execute(filters=None, additional_table_columns=None):
}
total_tax = 0
row.update(default_taxes.copy())
for tax in tax_columns:
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
row.update(

View File

@@ -29,8 +29,12 @@ 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, additional_table_columns, additional_conditions)
default_taxes = {}
if item_list:
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
for tax in tax_columns:
default_taxes[f"{tax}_rate"] = 0
default_taxes[f"{tax}_amount"] = 0
mode_of_payments = get_mode_of_payments(set(d.parent for d in item_list))
so_dn_map = get_delivery_notes_against_sales_order(item_list)
@@ -88,6 +92,8 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions=
total_tax = 0
total_other_charges = 0
row.update(default_taxes.copy())
for tax in tax_columns:
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
row.update(

View File

@@ -17,9 +17,11 @@ class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase):
def tearDown(self):
frappe.db.rollback()
def create_sales_invoice(self, do_not_submit=False):
def create_sales_invoice(self, item=None, taxes=None, do_not_submit=False):
si = create_sales_invoice(
item=self.item,
item=item or self.item,
item_name=item or self.item,
description=item or self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
@@ -30,6 +32,19 @@ class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase):
price_list_rate=100,
do_not_save=1,
)
for tax in taxes or []:
si.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": tax["account_head"],
"cost_center": self.cost_center,
"description": tax["description"],
"rate": tax["rate"],
},
)
si = si.save()
if not do_not_submit:
si = si.submit()
@@ -63,3 +78,50 @@ class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase):
report_output = {k: v for k, v in report[1][0].items() if k in expected_result}
self.assertDictEqual(report_output, expected_result)
def test_grouped_report_handles_different_tax_descriptions(self):
self.create_item(item_name="_Test Item Tax Description A")
first_item = self.item
self.create_item(item_name="_Test Item Tax Description B")
second_item = self.item
first_tax_description = "Tax Description A"
second_tax_description = "Tax Description B"
first_tax_amount_field = f"{frappe.scrub(first_tax_description)}_amount"
second_tax_amount_field = f"{frappe.scrub(second_tax_description)}_amount"
self.create_sales_invoice(
item=first_item,
taxes=[
{
"account_head": "_Test Account VAT - _TC",
"description": first_tax_description,
"rate": 5,
}
],
)
self.create_sales_invoice(
item=second_item,
taxes=[
{
"account_head": "_Test Account Service Tax - _TC",
"description": second_tax_description,
"rate": 2,
}
],
)
filters = frappe._dict(
{
"from_date": today(),
"to_date": today(),
"company": self.company,
"group_by": "Customer",
}
)
_, data, _, _, _, _ = execute(filters)
grand_total_row = next(row for row in data if row.get("bold") and row.get("item_code") == "Total")
self.assertEqual(grand_total_row[first_tax_amount_field], 5.0)
self.assertEqual(grand_total_row[second_tax_amount_field], 2.0)

View File

@@ -22,7 +22,7 @@ frappe.query_reports["Profit and Loss Statement"]["filters"].push(
fieldname: "accumulated_values",
label: __("Accumulated Values"),
fieldtype: "Check",
default: 1,
default: 0,
},
{
fieldname: "include_default_book_entries",

View File

@@ -7,10 +7,10 @@
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 3,
"idx": 4,
"is_standard": "Yes",
"letterhead": null,
"modified": "2025-11-05 11:55:49.950442",
"letter_head": null,
"modified": "2026-03-13 17:35:39.703838",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Trends",

View File

@@ -501,7 +501,7 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts, inc
else sum(base_tax_amount_after_discount_amount) * -1 end as tax_amount
from `tabPurchase Taxes and Charges`
where parent in (%s) and category in ('Total', 'Valuation and Total')
and base_tax_amount_after_discount_amount != 0
and base_tax_amount_after_discount_amount != 0 and parenttype='Purchase Invoice'
group by parent, account_head, add_deduct_tax
"""
% ", ".join(["%s"] * len(invoice_list)),

View File

@@ -6,6 +6,7 @@ from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_months, today
from erpnext.accounts.report.purchase_register.purchase_register import execute
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
class TestPurchaseRegister(FrappeTestCase):
@@ -26,6 +27,52 @@ class TestPurchaseRegister(FrappeTestCase):
self.assertEqual(first_row.total_tax, 100)
self.assertEqual(first_row.grand_total, 1100)
def test_purchase_register_ignores_tax_rows_from_other_doctype(self):
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'")
filters = frappe._dict(company="_Test Company 6", from_date=add_months(today(), -1), to_date=today())
pi = make_purchase_invoice()
# Real workflow setup: create a Purchase Receipt tax row in the same shared child table.
pr = make_purchase_receipt(
company="_Test Company 6",
supplier="_Test Supplier",
item="_Test Item",
warehouse="_Test Warehouse - _TC6",
cost_center="_Test Cost Center - _TC6",
do_not_save=1,
do_not_submit=1,
qty=1,
rate=1000,
)
pr.append(
"taxes",
{
"account_head": "GST - _TC6",
"cost_center": "_Test Cost Center - _TC6",
"add_deduct_tax": "Add",
"category": "Valuation and Total",
"charge_type": "Actual",
"description": "PR Tax",
"tax_amount": 100.0,
"rate": 100,
},
)
pr.insert()
pr.submit()
# Mimic custom naming collision across doctypes (same parent value in shared child table).
frappe.rename_doc("Purchase Receipt", pr.name, pi.name, force=True)
report_results = execute(filters)
first_row = frappe._dict(report_results[1][0])
self.assertEqual(first_row.voucher_no, pi.name)
self.assertEqual(first_row.total_tax, 100)
self.assertEqual(first_row.grand_total, 1100)
def test_purchase_register_ledger_view(self):
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'")

View File

@@ -9,8 +9,8 @@
"filters": [],
"idx": 3,
"is_standard": "Yes",
"letterhead": null,
"modified": "2025-11-05 11:55:50.070651",
"letter_head": null,
"modified": "2026-03-13 17:36:13.725601",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Trends",

View File

@@ -5,6 +5,7 @@ from frappe.utils import getdate, today
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.sales_register.sales_register import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase):
@@ -75,6 +76,43 @@ class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase):
report_output = {k: v for k, v in res[0].items() if k in expected_result}
self.assertDictEqual(report_output, expected_result)
def test_sales_register_ignores_tax_rows_from_other_doctype(self):
si = self.create_sales_invoice(rate=98)
# Real workflow setup: create a Sales Order with taxes in the shared child table.
so = make_sales_order(
item=self.item,
company=self.company,
customer=self.customer,
rate=77,
do_not_save=1,
do_not_submit=1,
)
so.append(
"taxes",
{
"charge_type": "Actual",
"account_head": self.income_account,
"description": "SO Tax",
"tax_amount": 55.0,
},
)
so.insert()
so.submit()
# Mimic custom naming collision across doctypes (same parent value in shared child table).
frappe.rename_doc("Sales Order", so.name, si.name, force=True)
filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company})
report = execute(filters)
res = [x for x in report[1] if x.get("voucher_no") == si.name]
self.assertEqual(len(res), 1)
result = frappe._dict(res[0])
self.assertEqual(result.net_total, 98.0)
self.assertEqual(result.tax_total, 0)
self.assertEqual(result.grand_total, 98.0)
def test_journal_with_cost_center_filter(self):
je1 = frappe.get_doc(
{

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