Compare commits

...

553 Commits

Author SHA1 Message Date
Frappe PR Bot
25ee3695f0 chore(release): Bumped to Version 15.114.0
# [15.114.0](https://github.com/frappe/erpnext/compare/v15.113.0...v15.114.0) (2026-06-24)

### Features

* **accounts:** add configurable job timeout for Process Period Closing Voucher ([82a8581](82a85818c2))
2026-06-24 10:25:43 +00:00
ruthra kumar
ff205da810 Merge pull request #56426 from frappe/mergify/bp/version-15/pr-56418
refactor: configurable timeout on process pcv (backport #56417) (backport #56418)
2026-06-24 15:48:58 +05:30
ruthra kumar
2980171007 chore: resolve conflicts
(cherry picked from commit df3c821f98)
2026-06-24 09:56:59 +00:00
ruthra kumar
c6c4815e8d refactor: patch, display depends on and json changes
(cherry picked from commit 3da7eefebb)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.py
#	erpnext/patches.txt
(cherry picked from commit c33d7e5d7b)
2026-06-24 09:56:59 +00:00
ruthra kumar
82a85818c2 feat(accounts): add configurable job timeout for Process Period Closing Voucher
Adds a `pcv_job_timeout` Int field (default 3600s) to Accounts Settings
so admins can tune the enqueue timeout for PCV background jobs without
a code change. All three `frappe.enqueue` calls in
`process_period_closing_voucher.py` now read this value at runtime.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 13b6c4a165)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
(cherry picked from commit c97be8abe1)
2026-06-24 09:56:58 +00:00
Frappe PR Bot
57a2be6b56 chore(release): Bumped to Version 15.113.0
# [15.113.0](https://github.com/frappe/erpnext/compare/v15.112.0...v15.113.0) (2026-06-23)

### Bug Fixes

* add dynamic links for customer and supplier dashboards ([690adf1](690adf1051))
* Add likely missing escapes (backport [#55574](https://github.com/frappe/erpnext/issues/55574)) ([#55580](https://github.com/frappe/erpnext/issues/55580)) ([ce8fce7](ce8fce78f1))
* add partially transferred status and fix button visibility for partial material transfer on job card ([1f5283d](1f5283da58))
* add validation and tests for set_status ([7bea925](7bea925230))
* apply docstatus filter to exclude cancelled Work Orders in Serial No ([90fd057](90fd057fb3))
* attribute error because of missing margin_type field in Supplier Quotation (backport [#48089](https://github.com/frappe/erpnext/issues/48089))  ([506658c](506658c3a6))
* **budget:** ambiguous error message for budget assignment validation (backport [#56390](https://github.com/frappe/erpnext/issues/56390)) ([#56391](https://github.com/frappe/erpnext/issues/56391)) ([53a1122](53a11229ec))
* clear stale payment rows on non-POS returns so they don't surface in bank reconciliation (backport [#55903](https://github.com/frappe/erpnext/issues/55903)) ([#56169](https://github.com/frappe/erpnext/issues/56169)) ([37d2622](37d26222d7))
* disable is_debit_note while creating credit note ([e4370ab](e4370ab332))
* **err:** add missing permission check on `get_account_details` ([041a9ad](041a9adbbf))
* escape user image url on various templates (backport [#56269](https://github.com/frappe/erpnext/issues/56269)) ([#56270](https://github.com/frappe/erpnext/issues/56270)) ([42af4ce](42af4ce7b0))
* **manufacturing:** make item_code mandatory in Job Card Item ([1b4da9d](1b4da9dc96))
* **payment_entry:** recompute base amount when exchange rate changes (backport [#56136](https://github.com/frappe/erpnext/issues/56136)) ([#56397](https://github.com/frappe/erpnext/issues/56397)) ([cef608d](cef608d043))
* **pos:** remove redundant opening balance dialog onchange handler (backport [#54591](https://github.com/frappe/erpnext/issues/54591)) ([#56402](https://github.com/frappe/erpnext/issues/56402)) ([334a0b2](334a0b2137))
* preserve stock ageing on non-serial reconciliation ([1991312](19913127a7))
* **report_utils:** remove unnecessary whitelist decorator on `get_invoiced_item_gross_margin` ([0efebf5](0efebf5d8c))
* resolve backport conflict in accounting dashboard number cards ([f106513](f106513005)), closes [#55548](https://github.com/frappe/erpnext/issues/55548) [#55484](https://github.com/frappe/erpnext/issues/55484)
* set a fallback value if no fiscal year set ([da1ccc2](da1ccc2b62))
* show contextual balance label on party dashboard for net balances ([9b6adc4](9b6adc42b6))
* simplify get_round_off_applicable_accounts function signature ([42121f2](42121f2e36))
* **stock:** allow partial raw material picking/transfer from work order ([a858d77](a858d77461))
* **stock:** apply precision to the additional cost amount in stock entry ([acc1444](acc1444c03))
* **stock:** propagate renamed attribute values to variant items ([27d574d](27d574dad5))
* **stock:** update transfer status for mixed transfer flows ([3f9a88a](3f9a88a5e2))
* **stock:** update variant attributes on value rename ([c7acd88](c7acd88742))
* **stock:** update voucher valuaion rate in sle (backport [#55960](https://github.com/frappe/erpnext/issues/55960)) ([#56262](https://github.com/frappe/erpnext/issues/56262)) ([37f847e](37f847e730))
* tax.base_tax_amount as none when payment entry created using API ([37dffa7](37dffa7273))
* update reference doctype mapping and field visibility in bank guarantee ([e556cbb](e556cbbe6a))
* update round off account functions to accept document context for regional overrides ([#55758](https://github.com/frappe/erpnext/issues/55758)) ([eef075a](eef075a2ba))
* use fiscal year instead of calendar year in accounting dashboard number cards ([81ce5fb](81ce5fbee9))

### Features

* add batch-level option to allow negative stock for batch ([5c4f19e](5c4f19ebdc))
2026-06-23 21:37:17 +00:00
Diptanil Saha
47f54a4725 Merge pull request #56360 from frappe/version-15-hotfix
chore: release v15
2026-06-24 03:05:42 +05:30
Diptanil Saha
a38da57c9e Merge branch 'version-15' into version-15-hotfix 2026-06-24 02:41:55 +05:30
mergify[bot]
334a0b2137 fix(pos): remove redundant opening balance dialog onchange handler (backport #54591) (#56402)
Co-authored-by: Ravibharathi <131471282+ravibharathi656@users.noreply.github.com>
fix(pos): remove redundant opening balance dialog onchange handler (#54591)
2026-06-24 02:24:29 +05:30
mergify[bot]
cef608d043 fix(payment_entry): recompute base amount when exchange rate changes (backport #56136) (#56397)
Co-authored-by: Ravibharathi <131471282+ravibharathi656@users.noreply.github.com>
Co-authored-by: ervishnucs <ervishnucs369@gmail.com>
fix(payment_entry): recompute base amount when exchange rate changes (#56136)
2026-06-24 02:10:54 +05:30
mergify[bot]
53a11229ec fix(budget): ambiguous error message for budget assignment validation (backport #56390) (#56391)
Co-authored-by: Wolfram Schmidt <wolfram.schmidt@phamos.eu>
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
fix(budget): ambiguous error message for budget assignment validation (#56390)
2026-06-23 19:49:07 +00:00
Shllokkk
094bdf2d3e Merge pull request #56385 from frappe/mergify/bp/version-15-hotfix/pr-56337
fix: show contextual balance label on party dashboard for net balances (backport #56337)
2026-06-23 23:31:19 +05:30
mergify[bot]
66d0a60140 feat(crm_settings)!: enable frappe crm data synchronization (backport #56268) (#56383)
* feat(crm_settings): enable frappe crm data synchronization (#56268)

(cherry picked from commit 0ab812c3ec)

# Conflicts:
#	erpnext/crm/frappe_crm_api.py

* chore: resolve conflicts

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2026-06-23 22:02:47 +05:30
Shllokkk
9b6adc42b6 fix: show contextual balance label on party dashboard for net balances
(cherry picked from commit 3251b40365)
2026-06-23 15:53:02 +00:00
Mihir Kandoi
e5894b67bc Merge pull request #56373 from frappe/mergify/bp/version-15-hotfix/pr-56364
fix(manufacturing): make item_code mandatory in Job Card Item (backport #56364)
2026-06-23 19:36:33 +05:30
pandiyan
1b4da9dc96 fix(manufacturing): make item_code mandatory in Job Card Item
The item_code field in the Job Card Item child table was optional,
allowing job cards to be saved without a raw material item linked.
Set reqd=1 in the JSON and update the Python type annotation accordingly.

(cherry picked from commit d7e9a97f8a)
2026-06-23 13:32:41 +00:00
Khushi Rawat
f75e7a3dc8 Merge pull request #55548 from frappe/mergify/bp/version-15-hotfix/pr-55484
fix: use fiscal year instead of calendar year in accounting dashboard number cards (backport #55484)
2026-06-23 16:50:55 +05:30
khushi8112
f106513005 fix: resolve backport conflict in accounting dashboard number cards
Resolved Mergify backport conflicts (PR #55548 / backport of #55484) by
taking the final upstream state: fiscal-year posting_date Between filter
with calendar-year fallback, Timespan filter removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 16:32:16 +05:30
mergify[bot]
506658c3a6 fix: attribute error because of missing margin_type field in Supplier Quotation (backport #48089)
* fix: add missing margin related fields

(cherry picked from commit 67f96c66e4)

# Conflicts:
#	erpnext/buying/doctype/supplier_quotation_item/supplier_quotation_item.json

* chore: fix conflict

---------

Co-authored-by: iamkhanraheel <raheel@frappe.io>
Co-authored-by: Sagar Vora <16315650+sagarvora@users.noreply.github.com>
2026-06-23 14:49:58 +05:30
Lakshit Jain
4048f0666e Merge pull request #55770 from frappe/mergify/bp/version-15-hotfix/pr-55758
fix: update round off account functions to accept document context for regional overrides (backport #55758)
2026-06-22 18:13:50 +05:30
ljain112
42121f2e36 fix: simplify get_round_off_applicable_accounts function signature 2026-06-22 17:40:57 +05:30
Mihir Kandoi
94d7d8f9d6 Merge pull request #56287 from aerele/backport-56203
fix: add partially transferred status and fix button visibility for p…
2026-06-22 16:48:04 +05:30
ruthra kumar
5ca37b8c37 Merge pull request #56298 from frappe/mergify/bp/version-15-hotfix/pr-55488
fix: add validation and tests for set_status (backport #55488)
2026-06-22 13:58:43 +05:30
Shllokkk
7bea925230 fix: add validation and tests for set_status
(cherry picked from commit b5a84c5e65)
2026-06-22 07:50:39 +00:00
pandiyan
1f5283da58 fix: add partially transferred status and fix button visibility for partial material transfer on job card 2026-06-22 11:51:33 +05:30
mergify[bot]
37d26222d7 fix: clear stale payment rows on non-POS returns so they don't surface in bank reconciliation (backport #55903) (#56169)
fix: clear stale payment rows on non-POS returns so they don't surface in bank reconciliation (#55903)

(cherry picked from commit 322d4dff25)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
#	erpnext/accounts/doctype/sales_invoice/services/pos.py

Co-authored-by: Jatin3128 <140256508+Jatin3128@users.noreply.github.com>
2026-06-22 05:17:49 +05:30
mergify[bot]
42af4ce7b0 fix: escape user image url on various templates (backport #56269) (#56270)
Co-authored-by: Diptanil Saha <diptanil@frappe.io>
fix: escape user image url on various templates (#56269)
2026-06-22 03:00:50 +05:30
Mihir Kandoi
67925bde42 Merge pull request #56264 from frappe/mergify/bp/version-15-hotfix/pr-56206
fix(stock): allow partial raw material picking/transfer from work order (backport #56206)
2026-06-22 01:12:01 +05:30
Sudharsanan11
e6d45e35c7 test(stock): add test to validate the partial transfer of raw material
(cherry picked from commit 4d055d374a)
2026-06-21 23:36:43 +05:30
Sudharsanan11
a858d77461 fix(stock): allow partial raw material picking/transfer from work order
(cherry picked from commit 8e3fbab94a)

# Conflicts:
#	erpnext/manufacturing/doctype/work_order/work_order.py
2026-06-21 23:36:43 +05:30
mergify[bot]
37f847e730 fix(stock): update voucher valuaion rate in sle (backport #55960) (#56262)
fix(stock): update voucher valuaion rate in sle (#55960)

(cherry picked from commit 130c2594e1)

Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
2026-06-21 22:07:38 +05:30
Mihir Kandoi
3d46ae7cbc Merge pull request #56248 from frappe/mergify/bp/version-15-hotfix/pr-56201
fix(stock): apply precision to the additional cost amount in stock entry (backport #56201)
2026-06-21 14:01:45 +05:30
Sudharsanan11
deb11745e5 test(stock): add test to validate the precision for additional cost amount
(cherry picked from commit 20b14395e3)
2026-06-21 08:14:47 +00:00
Sudharsanan11
acc1444c03 fix(stock): apply precision to the additional cost amount in stock entry
(cherry picked from commit 6ac699d3bb)
2026-06-21 08:14:47 +00:00
Diptanil Saha
a0c062af90 Merge pull request #56193 from frappe/mergify/bp/version-15-hotfix/pr-56191
fix: added missing permission validation on whitelisted function and removed unnecessary whitelisted decorator (backport #56191)
2026-06-20 20:19:27 +05:30
diptanilsaha
7a04f031d9 chore: resolve conflicts 2026-06-20 20:00:29 +05:30
diptanilsaha
0efebf5d8c fix(report_utils): remove unnecessary whitelist decorator on get_invoiced_item_gross_margin
(cherry picked from commit e29535f29c)

# Conflicts:
#	erpnext/accounts/report/utils.py
2026-06-20 20:00:29 +05:30
diptanilsaha
041a9adbbf fix(err): add missing permission check on get_account_details
(cherry picked from commit 9bf1e847d2)
2026-06-20 20:00:23 +05:30
ruthra kumar
7b6dd42aa1 Merge pull request #56158 from frappe/mergify/bp/version-15-hotfix/pr-55265
fix: update reference doctype mapping and field visibility in bank guarantee (backport #55265)
2026-06-19 17:54:27 +05:30
nareshkannasln
e556cbbe6a fix: update reference doctype mapping and field visibility in bank guarantee
(cherry picked from commit b1de654dfd)

# Conflicts:
#	erpnext/accounts/doctype/bank_guarantee/bank_guarantee.json
2026-06-19 16:57:18 +05:30
Mihir Kandoi
ad9c16073e Merge pull request #56035 from aerele/fix/dashboard-party-dynamic-links 2026-06-19 11:09:39 +05:30
Smit Vora
ce2560e2fe Merge pull request #56122 from frappe/mergify/bp/version-15-hotfix/pr-56104
fix: base_tax_amount as none when payment entry created using API (backport #56104)
2026-06-19 09:25:41 +05:30
vorasmit
37dffa7273 fix: tax.base_tax_amount as none when payment entry created using API
(cherry picked from commit b9b402f2ec)
2026-06-19 03:17:39 +00:00
Mihir Kandoi
3ef6475249 Merge pull request #56114 from frappe/mergify/bp/version-15-hotfix/pr-56055
fix: disable is_debit_note while creating credit note (backport #56055)
2026-06-18 22:53:32 +05:30
Mihir Kandoi
6deff470d8 Merge pull request #56065 from barredterra/item-update-attribute-value
fix(stock): propagate renamed attribute values to variant items
2026-06-18 22:35:17 +05:30
Mihir Kandoi
03cda066a5 Merge pull request #56116 from frappe/mergify/bp/version-15-hotfix/pr-56098
fix: apply docstatus filter to exclude cancelled Work Orders in Seria… (backport #56098)
2026-06-18 22:30:44 +05:30
pandiyan
90fd057fb3 fix: apply docstatus filter to exclude cancelled Work Orders in Serial No
(cherry picked from commit 3ba8f690a4)
2026-06-18 16:56:24 +00:00
pandiyan
e4370ab332 fix: disable is_debit_note while creating credit note
(cherry picked from commit 279c8dea06)
2026-06-18 16:54:01 +00:00
mergify[bot]
ce8fce78f1 fix: Add likely missing escapes (backport #55574) (#55580)
* fix: Add likely missing escaps (#55574)

(cherry picked from commit b72cde73ba)

# Conflicts:
#	erpnext/accounts/doctype/budget/budget.py
#	erpnext/controllers/website_list_for_contact.py

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2026-06-18 11:32:54 +00:00
rohitwaghchaure
f3334eb2d3 Merge pull request #56080 from rohitwaghchaure/allow-negative-stock-for-batch-v15
feat: allow negative stock at batch level (backport to v15)
2026-06-18 14:31:21 +05:30
Rohit Waghchaure
5c4f19ebdc feat: add batch-level option to allow negative stock for batch 2026-06-18 14:00:25 +05:30
rohitwaghchaure
80de914b55 Merge pull request #56076 from frappe/mergify/bp/version-15/pr-56072
refactor: posting datetime for SABB (backport #49710 + #50248) (backport #56072)
2026-06-18 11:49:23 +05:30
rohitwaghchaure
1444837653 refactor: posting datetime for SABB (backport #49710 + #50248) (#56072)
* refactor: posting datetime for SABB (backport #49710 + #50248)

Backport of #49710 "refactor: posting datetime for SABB" to
version-15-hotfix, including the regression fix #50248
"fix: missed refactoring code".

Replaces Serial and Batch Bundle's `posting_date`/`posting_time` with a
single `posting_datetime` field and updates all readers/writers; adds a
patch to populate `posting_datetime` from the linked Stock Ledger Entry
and drop the now-unused single-column indexes.

Adapted to version-15-hotfix where the surrounding stock code had already
moved to `posting_datetime` (SLE) and diverged from develop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: fix conflicts

* chore: fixed formatting

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 98caefea88)
2026-06-18 05:58:57 +00:00
rohitwaghchaure
98caefea88 refactor: posting datetime for SABB (backport #49710 + #50248) (#56072)
* refactor: posting datetime for SABB (backport #49710 + #50248)

Backport of #49710 "refactor: posting datetime for SABB" to
version-15-hotfix, including the regression fix #50248
"fix: missed refactoring code".

Replaces Serial and Batch Bundle's `posting_date`/`posting_time` with a
single `posting_datetime` field and updates all readers/writers; adds a
patch to populate `posting_datetime` from the linked Stock Ledger Entry
and drop the now-unused single-column indexes.

Adapted to version-15-hotfix where the surrounding stock code had already
moved to `posting_datetime` (SLE) and diverged from develop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: fix conflicts

* chore: fixed formatting

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 05:51:13 +00:00
barredterra
c7acd88742 fix(stock): update variant attributes on value rename 2026-06-17 17:55:27 +02:00
barredterra
60f5de7ab8 test(stock): add cleanup for item attribute value changes in tests 2026-06-17 17:34:42 +02:00
barredterra
27d574dad5 fix(stock): propagate renamed attribute values to variant items 2026-06-17 17:04:58 +02:00
Mihir Kandoi
5c4220ee77 Merge pull request #56052 from aerele/backport-56002
fix(stock): update transfer status for mixed transfer flows
2026-06-17 17:42:07 +05:30
pandiyan
c13567228e test(stock): validate completed status for mixed transfer methods 2026-06-17 17:22:53 +05:30
pandiyan
3f9a88a5e2 fix(stock): update transfer status for mixed transfer flows 2026-06-17 17:22:48 +05:30
Mihir Kandoi
8b2204ce69 Merge pull request #56044 from mihir-kandoi/codex/fix-stock-ageing-reco-ageing-v15
fix: preserve stock ageing on non-serial reconciliation
2026-06-17 16:19:17 +05:30
Mihir Kandoi
19913127a7 fix: preserve stock ageing on non-serial reconciliation 2026-06-17 15:58:47 +05:30
S Sakthivel Murugan
690adf1051 fix: add dynamic links for customer and supplier dashboards 2026-06-17 12:51:35 +05:30
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
ljain112
5629056ec2 chore: resolve conflicts 2026-06-09 18:12:53 +05:30
Lakshit Jain
eef075a2ba fix: update round off account functions to accept document context for regional overrides (#55758)
(cherry picked from commit 08129ff71c)

# Conflicts:
#	erpnext/controllers/taxes_and_totals.py
#	erpnext/controllers/tests/test_taxes_and_totals.py
#	erpnext/public/js/controllers/taxes_and_totals.js
2026-06-09 12:24:31 +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
khushi8112
da1ccc2b62 fix: set a fallback value if no fiscal year set
(cherry picked from commit c68918bc18)

# Conflicts:
#	erpnext/accounts/number_card/total_incoming_bills/total_incoming_bills.json
#	erpnext/accounts/number_card/total_incoming_payment/total_incoming_payment.json
#	erpnext/accounts/number_card/total_outgoing_bills/total_outgoing_bills.json
#	erpnext/accounts/number_card/total_outgoing_payment/total_outgoing_payment.json
2026-06-02 11:26:05 +00:00
khushi8112
81ce5fbee9 fix: use fiscal year instead of calendar year in accounting dashboard number cards
(cherry picked from commit e8fff2fdad)

# Conflicts:
#	erpnext/accounts/number_card/total_incoming_bills/total_incoming_bills.json
#	erpnext/accounts/number_card/total_incoming_payment/total_incoming_payment.json
#	erpnext/accounts/number_card/total_outgoing_bills/total_outgoing_bills.json
#	erpnext/accounts/number_card/total_outgoing_payment/total_outgoing_payment.json
2026-06-02 11:26:05 +00:00
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
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
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
342 changed files with 12390 additions and 5112 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.103.1"
__version__ = "15.114.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

@@ -79,6 +79,7 @@
"acc_frozen_upto",
"ignore_account_closing_balance",
"use_legacy_controller_for_pcv",
"pcv_job_timeout",
"column_break_25",
"frozen_accounts_modifier",
"tab_break_dpet",
@@ -95,7 +96,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",
@@ -561,7 +561,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 +623,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",
@@ -659,6 +652,14 @@
"fieldtype": "Check",
"label": "Show Party Balance"
},
{
"default": "3600",
"depends_on": "eval: !doc.use_legacy_controller_for_pcv",
"description": "Timeout (in seconds) for each background job enqueued by Process Period Closing Voucher",
"fieldname": "pcv_job_timeout",
"fieldtype": "Int",
"label": "PCV Job Timeout (seconds)"
},
{
"default": "30, 60, 90, 120",
"fieldname": "default_ageing_range",
@@ -671,7 +672,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2026-03-06 14:49:11.467716",
"modified": "2026-06-24 12:59:41.868865",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
@@ -701,4 +702,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -60,7 +60,8 @@ 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"]
pcv_job_timeout: DF.Int
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 +171,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

@@ -90,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:

View File

@@ -9,6 +9,13 @@ cur_frm.add_fetch("bank", "swift_number", "swift_number");
frappe.ui.form.on("Bank Guarantee", {
setup: function (frm) {
frm.set_query("reference_doctype", function () {
return {
filters: {
name: ["in", ["Sales Order", "Purchase Order"]],
},
};
});
frm.set_query("bank_account", function () {
return {
filters: {

View File

@@ -1,5 +1,6 @@
{
"actions": [],
"allow_bulk_edit": 1,
"autoname": "ACC-BG-.YYYY.-.#####",
"creation": "2016-12-17 10:43:35.731631",
"doctype": "DocType",
@@ -50,8 +51,7 @@
"fieldname": "reference_doctype",
"fieldtype": "Link",
"label": "Reference Document Type",
"options": "DocType",
"read_only": 1
"options": "DocType"
},
{
"fieldname": "reference_docname",
@@ -60,14 +60,14 @@
"options": "reference_doctype"
},
{
"depends_on": "eval: doc.bg_type == \"Receiving\"",
"depends_on": "eval: doc.reference_doctype == \"Sales Order\"",
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"options": "Customer"
},
{
"depends_on": "eval: doc.bg_type == \"Providing\"",
"depends_on": "eval: doc.reference_doctype == \"Purchase Order\"",
"fieldname": "supplier",
"fieldtype": "Link",
"label": "Supplier",
@@ -217,11 +217,11 @@
],
"is_submittable": 1,
"links": [],
"modified": "2025-09-26 00:38:17.584694",
"modified": "2026-05-25 18:12:10.768835",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Guarantee",
"naming_rule": "Expression (old style)",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{

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

@@ -103,8 +103,8 @@ class Budget(Document):
elif account_details.report_type != "Profit and Loss":
frappe.throw(
_(
"Budget cannot be assigned against {0}, as it's not an Income or Expense account"
).format(d.account)
"Budget cannot be assigned against {0}, as its Root Type is not of Income or Expense"
).format(self.account)
)
if d.account in account_list:
@@ -425,11 +425,11 @@ def get_ordered_amount(args):
def get_other_condition(args, for_doc):
condition = "expense_account = '%s'" % (args.expense_account)
condition = f"expense_account = {frappe.db.escape(args.expense_account)}"
budget_against_field = args.get("budget_against_field")
if budget_against_field and args.get(budget_against_field):
condition += f" and child.{budget_against_field} = '{args.get(budget_against_field)}'"
condition += f" and child.{budget_against_field} = {frappe.db.escape(args.get(budget_against_field))}"
if args.get("fiscal_year"):
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
@@ -437,8 +437,7 @@ def get_other_condition(args, for_doc):
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
)
condition += f""" and parent.{date_field}
between '{start_date}' and '{end_date}' """
condition += f" and parent.{date_field} between {frappe.db.escape(str(start_date))} and {frappe.db.escape(str(end_date))}"
return condition

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

@@ -619,6 +619,10 @@ def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
def get_account_details(
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float | None = None
):
if not account:
return
frappe.has_permission("Account", doc=account, throw=True)
if not (company and posting_date):
frappe.throw(_("Company and Posting Date is mandatory"))

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

@@ -1184,7 +1184,11 @@ class JournalEntry(AccountsController):
self.validate_total_debit_and_credit()
def get_values(self):
cond = f" and outstanding_amount <= {self.write_off_amount}" if flt(self.write_off_amount) > 0 else ""
cond = (
f" and outstanding_amount <= {flt(self.write_off_amount)}"
if flt(self.write_off_amount) > 0
else ""
)
if self.write_off_based_on == "Accounts Receivable":
return frappe.db.sql(

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

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

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,28 @@ 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.paid_amount && frm.doc.source_exchange_rate) {
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_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);
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);
frm.set_value("received_amount", frm.doc.paid_amount);
} else {
const target_rate =
flt(frm.doc.target_exchange_rate) ||
(company_currency == frm.doc.paid_to_account_currency ? 1 : 0);
if (target_rate) {
frm.set_value("received_amount", flt(frm.doc.base_received_amount) / target_rate);
}
}
// set_unallocated_amount is called by below method,
@@ -832,6 +792,37 @@ 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.received_amount && frm.doc.target_exchange_rate) {
frm.set_value(
"base_received_amount",
flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate)
);
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
if (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("paid_amount", frm.doc.received_amount);
} else {
const source_rate =
flt(frm.doc.source_exchange_rate) ||
(company_currency == frm.doc.paid_from_account_currency ? 1 : 0);
if (source_rate) {
frm.set_value("paid_amount", flt(frm.doc.base_paid_amount) / source_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 +831,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 +855,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 +1782,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

@@ -1197,9 +1197,9 @@ class PaymentEntry(AccountsController):
continue
if tax.add_deduct_tax == "Add":
included_taxes += tax.base_tax_amount
included_taxes += flt(tax.base_tax_amount)
else:
included_taxes -= tax.base_tax_amount
included_taxes -= flt(tax.base_tax_amount)
return included_taxes
@@ -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"):
@@ -2561,7 +2563,7 @@ def get_orders_to_be_billed(
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"
@@ -2789,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
@@ -3035,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
@@ -3600,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"
@@ -1090,6 +1118,27 @@ class TestPaymentEntry(FrappeTestCase):
self.assertEqual(gl_entries, expected_gl_entries)
def test_payment_entry_with_inclusive_tax(self):
# inclusive tax built server-side: base_tax_amount is None until apply_taxes()
payment_entry = create_payment_entry(paid_amount=1180)
payment_entry.append(
"taxes",
{
"account_head": "_Test Account Service Tax - _TC",
"charge_type": "On Paid Amount",
"rate": 18,
"included_in_paid_amount": 1,
"add_deduct_tax": "Add",
"description": "Service Tax",
},
)
payment_entry.save()
payment_entry.submit()
# 1180 incl 18% => 1000 base + 180 tax
self.assertEqual(flt(payment_entry.total_taxes_and_charges, 2), 180.0)
self.assertEqual(flt(payment_entry.unallocated_amount, 2), 1000.0)
def test_payment_entry_against_onhold_purchase_invoice(self):
pi = make_purchase_invoice()
@@ -1937,6 +1986,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 +2123,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

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

@@ -658,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,20 @@ 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,
timeout = frappe.db.get_single_value("Accounts Settings", "pcv_job_timeout") or 3600
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:
@@ -113,7 +120,7 @@ def start_pcv_processing(docname: str):
frappe.enqueue(
method="erpnext.accounts.doctype.process_period_closing_voucher.process_period_closing_voucher.process_individual_date",
queue="long",
timeout="3600",
timeout=timeout,
is_async=True,
enqueue_after_commit=True,
docname=docname,
@@ -130,9 +137,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 +174,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 +246,17 @@ 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,
timeout = frappe.db.get_single_value("Accounts Settings", "pcv_job_timeout") or 3600
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(
@@ -257,7 +273,7 @@ def schedule_next_date(docname: str):
frappe.enqueue(
method="erpnext.accounts.doctype.process_period_closing_voucher.process_period_closing_voucher.process_individual_date",
queue="long",
timeout="3600",
timeout=timeout,
is_async=True,
enqueue_after_commit=True,
docname=docname,
@@ -278,7 +294,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=timeout,
is_async=True,
job_name=job_name,
enqueue_after_commit=True,
docname=docname,
)
def make_dict_json_compliant(dimension_wise_balance) -> dict:
@@ -534,6 +564,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

@@ -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()
@@ -633,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 += _(
@@ -657,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:
@@ -737,6 +760,7 @@ class PurchaseInvoice(BuyingController):
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])
@@ -850,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,
@@ -978,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:
@@ -1146,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]
@@ -1183,6 +1217,7 @@ class PurchaseInvoice(BuyingController):
item=item,
)
)
if (
self.auto_accounting_for_stock
and self.is_opening == "No"
@@ -1524,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(
@@ -1538,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,
@@ -2050,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

@@ -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()
@@ -438,8 +455,8 @@ class SalesInvoice(SellingController):
self.calculate_taxes_and_totals()
def before_save(self):
self.set_account_for_mode_of_payment()
self.set_paid_amount()
self.set_account_for_mode_of_payment()
def before_submit(self):
self.add_remarks()
@@ -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
@@ -762,6 +791,13 @@ class SalesInvoice(SellingController):
def set_paid_amount(self):
paid_amount = 0.0
base_paid_amount = 0.0
if not cint(self.is_pos) and self.is_return:
self.set("payments", [])
self.paid_amount = paid_amount
self.base_paid_amount = base_paid_amount
return
for data in self.payments:
data.base_amount = flt(data.amount * self.conversion_rate, self.precision("base_paid_amount"))
paid_amount += data.amount
@@ -776,11 +812,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"""
@@ -1112,7 +1162,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()
@@ -2483,7 +2538,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"]:
@@ -2516,18 +2571,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", "cost_center"],
"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"
@@ -2540,20 +2602,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

View File

@@ -1049,6 +1049,21 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(pos_return.get("payments")[0].amount, -500)
self.assertEqual(pos_return.get("payments")[1].amount, -500)
def test_non_pos_return_clears_payment_rows(self):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
si = create_sales_invoice(do_not_save=True)
si.append("payments", {"mode_of_payment": "Cash", "amount": 100})
si.insert()
si.submit()
si_return = make_sales_return(si.name)
si_return.insert()
self.assertEqual(si_return.is_pos, 0)
self.assertEqual(si_return.get("payments"), [])
self.assertEqual(si_return.paid_amount, 0)
def test_pos_change_amount(self):
make_pos_profile(
company="_Test Company with perpetual inventory",
@@ -2690,6 +2705,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 +3021,7 @@ class TestSalesInvoice(FrappeTestCase):
si.submit()
# Check if adjustment entry is created
self.assertTrue(
self.assertFalse(
frappe.db.exists(
"GL Entry",
{
@@ -3230,7 +3334,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")

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"):

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

@@ -4,14 +4,14 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Purchase Invoice",
"dynamic_filters_json": "[[\"Purchase Invoice\",\"company\",\"=\",\" frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Purchase Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Purchase Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
"dynamic_filters_json": "[[\"Purchase Invoice\", \"company\", \"=\", \"frappe.defaults.get_user_default(\\\"Company\\\")\"], [\"Purchase Invoice\", \"posting_date\", \"Between\", \"(frappe.boot.current_fiscal_year || [null, `${frappe.datetime.get_today().slice(0,4)}-01-01`, `${frappe.datetime.get_today().slice(0,4)}-12-31`]).slice(1)\"]]",
"filters_json": "[[\"Purchase Invoice\",\"docstatus\",\"=\",\"1\"]]",
"function": "Sum",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"label": "Total Incoming Bills",
"modified": "2024-11-20 19:08:37.043777",
"modified": "2026-06-01 12:00:00.000000",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Total Incoming Bills",

View File

@@ -4,14 +4,14 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Payment Entry",
"dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Receive\",false]]",
"dynamic_filters_json": "[[\"Payment Entry\", \"company\", \"=\", \"frappe.defaults.get_user_default(\\\"Company\\\")\"], [\"Payment Entry\", \"posting_date\", \"Between\", \"(frappe.boot.current_fiscal_year || [null, `${frappe.datetime.get_today().slice(0,4)}-01-01`, `${frappe.datetime.get_today().slice(0,4)}-12-31`]).slice(1)\"]]",
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\"],[\"Payment Entry\",\"payment_type\",\"=\",\"Receive\"]]",
"function": "Sum",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"label": "Total Incoming Payment",
"modified": "2020-07-22 13:06:20.237689",
"modified": "2026-06-01 12:00:00.000000",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Total Incoming Payment",

View File

@@ -4,14 +4,14 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Sales Invoice",
"dynamic_filters_json": "[[\"Sales Invoice\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Sales Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Sales Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
"dynamic_filters_json": "[[\"Sales Invoice\", \"company\", \"=\", \"frappe.defaults.get_user_default(\\\"Company\\\")\"], [\"Sales Invoice\", \"posting_date\", \"Between\", \"(frappe.boot.current_fiscal_year || [null, `${frappe.datetime.get_today().slice(0,4)}-01-01`, `${frappe.datetime.get_today().slice(0,4)}-12-31`]).slice(1)\"]]",
"filters_json": "[[\"Sales Invoice\",\"docstatus\",\"=\",\"1\"]]",
"function": "Sum",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"label": "Total Outgoing Bills",
"modified": "2020-07-22 13:07:19.633101",
"modified": "2026-06-01 12:00:00.000000",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Total Outgoing Bills",

View File

@@ -4,14 +4,14 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Payment Entry",
"dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Pay\",false]]",
"dynamic_filters_json": "[[\"Payment Entry\", \"company\", \"=\", \"frappe.defaults.get_user_default(\\\"Company\\\")\"], [\"Payment Entry\", \"posting_date\", \"Between\", \"(frappe.boot.current_fiscal_year || [null, `${frappe.datetime.get_today().slice(0,4)}-01-01`, `${frappe.datetime.get_today().slice(0,4)}-12-31`]).slice(1)\"]]",
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\"],[\"Payment Entry\",\"payment_type\",\"=\",\"Pay\"]]",
"function": "Sum",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"label": "Total Outgoing Payment",
"modified": "2020-07-22 12:49:34.942896",
"modified": "2026-06-01 12:00:00.000000",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Total Outgoing Payment",

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)
@@ -900,6 +922,15 @@ def get_dashboard_info(party_type, party, loyalty_program=None):
if party_type == "Supplier":
info["total_unpaid"] = -1 * info["total_unpaid"]
if info["total_unpaid"] < 0:
info["balance_label"] = (
"Total Advance Paid" if party_type == "Supplier" else "Total Advance Received"
)
info["balance_amount"] = abs(info["total_unpaid"])
else:
info["balance_label"] = "Total Unpaid"
info["balance_amount"] = info["total_unpaid"]
company_wise_info.append(info)
return company_wise_info

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

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

@@ -786,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:
@@ -1049,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

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

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

View File

@@ -119,8 +119,8 @@ def get_result(filters, tds_accounts, tax_category_map, net_total_map):
row.update(
{
"section_code": tax_withholding_category or "",
"entity_type": party_map.get(party, {}).get(party_type),
"tax_withholding_category": tax_withholding_category or "",
"party_entity_type": party_map.get(party, {}).get(party_type),
"rate": rate,
"total_amount": total_amount,
"grand_total": grand_total,
@@ -141,7 +141,7 @@ def get_result(filters, tds_accounts, tax_category_map, net_total_map):
else:
entries[key] = row
out = list(entries.values())
out.sort(key=lambda x: (x["section_code"], x["transaction_date"], x["ref_no"]))
out.sort(key=lambda x: (x["tax_withholding_category"], x["transaction_date"], x["ref_no"]))
return out
@@ -205,9 +205,9 @@ def get_columns(filters):
pan = "pan" if frappe.db.has_column(filters.party_type, "pan") else "tax_id"
columns = [
{
"label": _("Section Code"),
"label": _("Tax Withholding Category"),
"options": "Tax Withholding Category",
"fieldname": "section_code",
"fieldname": "tax_withholding_category",
"fieldtype": "Link",
"width": 90,
},
@@ -236,7 +236,12 @@ def get_columns(filters):
columns.extend(
[
{"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 100},
{
"label": _(f"{filters.get('party_type', 'Party')} Type"),
"fieldname": "party_entity_type",
"fieldtype": "Data",
"width": 100,
},
]
)
if filters.party_type == "Supplier":

View File

@@ -118,7 +118,7 @@ class TestTaxWithholdingDetails(AccountsTestMixin, FrappeTestCase):
voucher_expected_values = expected_values[i]
voucher_actual_values = (
voucher.ref_no,
voucher.section_code,
voucher.tax_withholding_category,
voucher.rate,
voucher.base_tax_withholding_net_total,
voucher.base_total,

View File

@@ -48,28 +48,25 @@ def group_by_party_and_category(data, filters):
party_category_wise_map = {}
for row in data:
key = (row.get("party_type"), row.get("party"), row.get("tax_withholding_category"))
party_category_wise_map.setdefault(
(row.get("party"), row.get("section_code")),
key,
{
"pan": row.get("pan"),
"tax_id": row.get("tax_id"),
"party": row.get("party"),
"party_type": row.get("party_type"),
"party_name": row.get("party_name"),
"section_code": row.get("section_code"),
"entity_type": row.get("entity_type"),
"tax_withholding_category": row.get("tax_withholding_category"),
"party_entity_type": row.get("party_entity_type"),
"rate": row.get("rate"),
"total_amount": 0.0,
"tax_amount": 0.0,
},
)
party_category_wise_map.get((row.get("party"), row.get("section_code")))["total_amount"] += row.get(
"total_amount", 0.0
)
party_category_wise_map.get((row.get("party"), row.get("section_code")))["tax_amount"] += row.get(
"tax_amount", 0.0
)
party_category_wise_map.get(key)["total_amount"] += row.get("total_amount", 0.0)
party_category_wise_map.get(key)["tax_amount"] += row.get("tax_amount", 0.0)
final_result = get_final_result(party_category_wise_map)
@@ -110,13 +107,18 @@ def get_columns(filters):
columns.extend(
[
{
"label": _("Section Code"),
"label": _("Tax Withholding Category"),
"options": "Tax Withholding Category",
"fieldname": "section_code",
"fieldname": "tax_withholding_category",
"fieldtype": "Link",
"width": 180,
},
{"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 180},
{
"label": _(f"{filters.get('party_type', 'Party')} Type"),
"fieldname": "party_entity_type",
"fieldtype": "Data",
"width": 180,
},
{
"label": _("TDS Rate %") if filters.get("party_type") == "Supplier" else _("TCS Rate %"),
"fieldname": "rate",

View File

@@ -146,7 +146,6 @@ def get_appropriate_company(filters):
return company
@frappe.whitelist()
def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=None, with_item_data=False):
from erpnext.accounts.report.gross_profit.gross_profit import GrossProfitGenerator

View File

@@ -12,6 +12,7 @@ class AccountsTestMixin:
customer = frappe.new_doc("Customer")
customer.customer_name = customer_name
customer.type = "Individual"
customer.customer_group = "Individual"
if currency:
customer.default_currency = currency
@@ -36,6 +37,7 @@ class AccountsTestMixin:
"account": default_account,
},
)
customer.customer_group = "Individual"
customer.save()
self.customer = customer_name

View File

@@ -89,6 +89,8 @@ class TestUtils(unittest.TestCase):
purchase_invoice.submit()
payment_entry = get_payment_entry(purchase_invoice.doctype, purchase_invoice.name)
payment_entry.target_exchange_rate = 82.32
payment_entry.set_amounts()
payment_entry.paid_amount = 15725
payment_entry.deductions = []
payment_entry.save()

View File

@@ -7,12 +7,8 @@ from erpnext.accounts.party import get_default_price_list
class PartyTestCase(FrappeTestCase):
def test_get_default_price_list_should_return_none_for_invalid_group(self):
customer = frappe.get_doc(
{
"doctype": "Customer",
"customer_name": "test customer",
}
{"doctype": "Customer", "customer_name": "test customer", "customer_group": "Individual"}
).insert(ignore_permissions=True, ignore_mandatory=True)
customer.customer_group = None
customer.save()
price_list = get_default_price_list(customer)
assert price_list is None

View File

@@ -176,7 +176,6 @@ def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None):
throw(_("{0} '{1}' not in Fiscal Year {2}").format(_(label), formatdate(date), fiscal_year))
@frappe.whitelist()
def get_balance_on(
account=None,
date=None,
@@ -278,6 +277,7 @@ def get_balance_on(
)
if party_type and party:
frappe.has_permission(party_type, "read", party, throw=True)
cond.append(
f"""gle.party_type = {frappe.db.escape(party_type)} and gle.party = {frappe.db.escape(party)} """
)
@@ -397,15 +397,13 @@ def add_ac(args=None):
if not args:
args = frappe.local.form_dict
args.pop("ignore_permissions", None)
frappe.has_permission("Account", "create", throw=True)
args.doctype = "Account"
args = make_tree_args(**args)
ac = frappe.new_doc("Account")
if args.get("ignore_permissions"):
ac.flags.ignore_permissions = True
args.pop("ignore_permissions")
ac.update(args)
if not ac.parent_account:
@@ -500,7 +498,7 @@ def reconcile_against_document(
skip_ref_details_update_for_pe=skip_ref_details_update_for_pe,
dimensions_dict=dimensions_dict,
)
if referenced_row.get("outstanding_amount"):
if referenced_row.get("outstanding_amount") and entry.get("outstanding_amount") is None:
referenced_row.outstanding_amount -= flt(entry.allocated_amount)
reposting_rows.append(referenced_row)
@@ -1388,6 +1386,7 @@ def update_cost_center(docname, cost_center_name, cost_center_number, company, m
Renames the document by adding the number as a prefix to the current name and updates
all transaction where it was present.
"""
frappe.has_permission("Cost Center", "write", doc=docname, throw=True)
validate_field_number("Cost Center", docname, cost_center_number, company, "cost_center_number")
if cost_center_number:
@@ -1934,8 +1933,9 @@ def create_payment_ledger_entry(
ple = frappe.get_doc(entry)
if cancel:
delink_original_entry(ple, partial_cancel=partial_cancel)
if is_immutable_ledger_enabled():
if not is_immutable_ledger_enabled():
delink_original_entry(ple, partial_cancel=partial_cancel)
else:
ple.delinked = 0
ple.posting_date = frappe.form_dict.get("posting_date") or getdate()
@@ -2027,6 +2027,7 @@ def delink_original_entry(pl_entry, partial_cancel=False):
qb.update(ple)
.set(ple.modified, now())
.set(ple.modified_by, frappe.session.user)
.set(ple.delinked, True)
.where(
(ple.company == pl_entry.company)
& (ple.account_type == pl_entry.account_type)
@@ -2043,9 +2044,6 @@ def delink_original_entry(pl_entry, partial_cancel=False):
if partial_cancel:
query = query.where(ple.voucher_detail_no == pl_entry.voucher_detail_no)
if not is_immutable_ledger_enabled():
query = query.set(ple.delinked, True)
query.run()
@@ -2320,6 +2318,7 @@ def create_gain_loss_journal(
ref2_detail_no,
cost_center,
dimensions,
project=None,
) -> str:
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = "Exchange Gain Or Loss"
@@ -2346,6 +2345,7 @@ def create_gain_loss_journal(
"account_currency": party_account_currency,
"exchange_rate": 0,
"cost_center": cost_center or erpnext.get_default_cost_center(company),
"project": project,
"reference_type": ref1_dt,
"reference_name": ref1_dn,
"reference_detail_no": ref1_detail_no,
@@ -2363,6 +2363,7 @@ def create_gain_loss_journal(
"account_currency": gain_loss_account_currency,
"exchange_rate": 1,
"cost_center": cost_center or erpnext.get_default_cost_center(company),
"project": project,
"reference_type": ref2_dt,
"reference_name": ref2_dn,
"reference_detail_no": ref2_detail_no,

View File

@@ -41,7 +41,7 @@ frappe.ui.form.on("Asset Movement", {
});
},
onload: (frm) => {
refresh: (frm) => {
frm.trigger("set_required_fields");
},

View File

@@ -31,7 +31,8 @@ class BulkTransactionLog(Document):
log_detail = qb.DocType("Bulk Transaction Log Detail")
has_records = frappe.db.sql(
f"select exists (select * from `tabBulk Transaction Log Detail` where date = '{self.name}');"
"select exists (select * from `tabBulk Transaction Log Detail` where date = %s);",
(self.name,),
)[0][0]
if not has_records:
raise frappe.DoesNotExistError

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