Compare commits

...

811 Commits

Author SHA1 Message Date
diptanilsaha
5d66be710f ci: preventing manual changes in erpnext/locale/* 2026-03-09 03:14:36 +05:30
diptanilsaha
f67cfc48e0 Merge pull request #53224 from diptanilsaha/missed_ta 2026-03-07 15:23:50 +05:30
diptanilsaha
56345f4354 refactor(book_appointment): type annotations for whitelisted methods 2026-03-07 15:00:34 +05:30
diptanilsaha
b51c59d20d refactor(templates): type annotations for whitelisted methods 2026-03-07 15:00:34 +05:30
diptanilsaha
decd9343ee refactor(call_log): type annotations for whitelisted methods 2026-03-07 15:00:34 +05:30
diptanilsaha
950bf682c3 refactor(support): type annotations for whitelisted methods 2026-03-07 15:00:34 +05:30
diptanilsaha
1f4d20413e refactor(serial_no): added missing type annotation on auto_fetch_serial_number 2026-03-07 15:00:34 +05:30
diptanilsaha
11a6db32ae refactor(leaderboard): type annotations for whitelisted methods 2026-03-07 15:00:34 +05:30
diptanilsaha
805fc807a9 refactor(work_order): added type annotations on cancel_stock_reservation_entries 2026-03-07 15:00:34 +05:30
diptanilsaha
b4fb74c84d refactor(asset): added type annotations on get_depreciation_rate 2026-03-07 15:00:28 +05:30
diptanilsaha
ac351bce4b Merge pull request #53222 from frappe/l10n_develop 2026-03-07 14:55:12 +05:30
diptanilsaha
208112e7a9 refactor(payment_request): type annotations for whitelisted methods 2026-03-07 11:44:16 +05:30
diptanilsaha
616bae1f84 refactor: type annotations for whitelisted methods 2026-03-07 11:42:16 +05:30
diptanilsaha
573eb25d5b Merge pull request #53022 from diptanilsaha/crm_ta 2026-03-07 11:41:26 +05:30
diptanilsaha
8267482ee9 refactor(crm): type annotations for whitelisted methods 2026-03-07 03:00:54 +05:30
diptanilsaha
a1c378c16f Merge pull request #53032 from diptanilsaha/setup_ta 2026-03-07 02:29:29 +05:30
diptanilsaha
e6bebbfe81 test: added the currency key and fixed company 2026-03-07 02:05:54 +05:30
MochaMind
9358a122cd fix: Russian translations 2026-03-06 23:08:42 +05:30
rohitwaghchaure
3bd023d640 Merge pull request #53218 from rohitwaghchaure/fixed-item-creation-in-test-cases
fix: HRMS test cases failing due to validation in item
2026-03-06 17:54:36 +05:30
Rohit Waghchaure
6702506f58 fix: HRMS test cases failing due to validation in item 2026-03-06 17:35:12 +05:30
diptanilsaha
913ce9454e fix(consolidated_financial_statement): convert report to presentation_currency if valid filter is set 2026-03-06 16:04:28 +05:30
diptanilsaha
24f5fb7b44 fix(sales_order): set valid target.currency in make_purchase_order 2026-03-06 16:04:28 +05:30
diptanilsaha
6aaca38ce3 refactor(setup): type annotataions for whitelisted methods 2026-03-06 16:04:28 +05:30
diptanilsaha
fbdb3a1f48 Merge pull request #53031 from diptanilsaha/regional_ta 2026-03-06 15:54:05 +05:30
Shubh Doshi
d2e039ad4e fix: add type hints to whitelisted functions (#53210) 2026-03-06 08:27:01 +00:00
rohitwaghchaure
d6abbce4ec Merge pull request #53200 from rohitwaghchaure/fixed-stock-balance-report
fix: stock balance report qty
2026-03-06 12:43:55 +05:30
rohitwaghchaure
f316229f9e Merge pull request #52549 from rohitwaghchaure/feat-option-to-enable-disable-serial-batch
feat: option to enable serial / batch feature
2026-03-06 12:17:11 +05:30
Rohit Waghchaure
a15e5fdc4e fix: stock balance report qty 2026-03-06 12:16:07 +05:30
Rohit Waghchaure
82c3da5b1e feat: option to enable serial / batch features 2026-03-06 11:47:53 +05:30
Soham Kulkarni
76d8e8fec9 Merge pull request #53177 from sokumon/add-clear-demo-data 2026-03-05 23:43:21 +05:30
Akhil Narang
702adda000 fix(help): escape query (#53192)
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
2026-03-05 13:06:09 +00:00
rohitwaghchaure
2bc3c146a6 Merge pull request #52956 from rohitwaghchaure/organization-desktop-icon
feat: organization desktop icon
2026-03-05 14:05:32 +05:30
rohitwaghchaure
ba05608ce6 Merge pull request #52745 from rohitwaghchaure/fixed-balance-qty-inv-dimension
fix: balance qty for inv dimension
2026-03-05 13:49:36 +05:30
Rohit Waghchaure
4e9a2a327f feat: organization desktop icon 2026-03-05 13:43:35 +05:30
rohitwaghchaure
06e0effbf7 Merge pull request #53170 from jacob-salvi/new-org-icon
chore: New org icon
2026-03-05 13:39:16 +05:30
Rohit Waghchaure
a3eafe5b18 fix: balance qty for inv dimension 2026-03-05 13:22:45 +05:30
Mihir Kandoi
50d6971d20 Merge pull request #53161 from mihir-kandoi/type-hint-issues 2026-03-05 12:06:26 +05:30
Nishka Gosalia
7e368c16b7 Merge pull request #52924 from nishkagosalia/projects-form-cleanup 2026-03-05 11:57:43 +05:30
Jatin3128
edac82f6e3 Merge pull request #52138 from aerele/payment-date-mismatch
feat(selling-settings): add checkbox to fetch payment terms
2026-03-05 11:48:43 +05:30
Nishka Gosalia
3c9f520e68 refactor: project module form cleanup 2026-03-05 11:40:22 +05:30
sokumon
6603005822 fix: add clear demo data in sidebar 2026-03-05 11:32:56 +05:30
Nishka Gosalia
15eb5d9827 Merge pull request #52415 from aerele/project-template-task-subject 2026-03-05 11:31:21 +05:30
ravibharathi656
7801dd5353 fix(task): allow is_template field in quick entry 2026-03-05 10:56:48 +05:30
ravibharathi656
c1431105f4 fix(project template): clear subject when task is empty 2026-03-05 10:56:48 +05:30
jacob-salvi
6f93210b9b chore: new organisation icon 2026-03-05 10:14:45 +05:30
SowmyaArunachalam
8aae46a25e test: test payment terms with backdated entries 2026-03-04 21:47:43 +05:30
SowmyaArunachalam
99ed1c34f3 fix: fetch payment terms from quotation 2026-03-04 21:47:41 +05:30
Mihir Kandoi
f07a6eb199 Merge pull request #53166 from mihir-kandoi/wo-producted-qty 2026-03-04 20:33:57 +05:30
Mihir Kandoi
5fead1d17a fix: WO produced qty should be calculated using finished item child table transfer qty 2026-03-04 20:17:37 +05:30
SowmyaArunachalam
8b9e02fd44 fix: set default to 1 2026-03-04 19:00:16 +05:30
SowmyaArunachalam
f23dc07914 fix: handle payment terms template when disabled 2026-03-04 19:00:15 +05:30
SowmyaArunachalam
5611e9168e test: enable automatically_fetch_payment_terms_from_quotation 2026-03-04 19:00:15 +05:30
SowmyaArunachalam
b1149fe950 fix: consider payment term only when enabled 2026-03-04 19:00:15 +05:30
SowmyaArunachalam
70b401e610 feat(selling-settings): add checkbox to recalculate payment date 2026-03-04 19:00:15 +05:30
Mihir Kandoi
58c198a58b Merge pull request #53157 from mihir-kandoi/gh53101 2026-03-04 16:51:09 +05:30
Khushi Rawat
f2decc852c Merge pull request #53154 from khushi8112/inter-company-asset-transfer-issue
fix: skip asset sale processing for internal transfer invoices
2026-03-04 16:36:03 +05:30
Mihir Kandoi
ee19c32c3a fix: disallow all actions on job card if work order is closed 2026-03-04 16:34:32 +05:30
Mihir Kandoi
74b658fabf fix: some type hints 2026-03-04 16:31:28 +05:30
Mihir Kandoi
083b571641 Merge pull request #53156 from nishkagosalia/st-61597 2026-03-04 16:12:24 +05:30
Nishka Gosalia
e37d4a6f7c fix: updating costing based on employee change in timesheet 2026-03-04 15:24:54 +05:30
khushi8112
9cb3dad079 fix: skip asset sale processing for internal transfer invoices 2026-03-04 14:20:14 +05:30
Mihir Kandoi
700c14d5b3 Merge pull request #53147 from nishkagosalia/gh-52102 2026-03-04 12:19:35 +05:30
Mihir Kandoi
c0148c7266 Merge pull request #53148 from mihir-kandoi/fg_completed_qty 2026-03-04 12:09:18 +05:30
Mihir Kandoi
2d83069b82 fix: do not update fg_completed_qty when changing qty of fg line item 2026-03-04 12:06:28 +05:30
Nishka Gosalia
2ec02e477f feat: allowing rate modification in update item in quotation 2026-03-04 10:54:51 +05:30
ruthra kumar
42ae954c16 Merge pull request #53005 from frappe/l10n_develop
fix: sync translations from crowdin
2026-03-04 10:45:47 +05:30
Mihir Kandoi
e3a55d31d3 Merge pull request #52784 from aerele/fix/support-#60042 2026-03-03 21:16:15 +05:30
Mihir Kandoi
767925a481 Merge pull request #53132 from aerele/fix/selling-price-validation 2026-03-03 21:09:30 +05:30
Mihir Kandoi
e970578c47 Merge pull request #53070 from aerele/rfq-email-template 2026-03-03 21:05:53 +05:30
kavin-114
723993fdf6 test: add unit test for FG Item selling price validation 2026-03-03 20:44:08 +05:30
kavin-114
4335318482 fix(selling): handle selling price validation for FG item
System checks valuation rate in incoming_rate field, since SO has it in valuation_rate field of item row,
need to handle the field name dynamically based upon the doctype name in Selling Controller.
2026-03-03 20:44:00 +05:30
Mihir Kandoi
17c715fde0 Merge pull request #53084 from aerele/fix/support-#61033 2026-03-03 20:40:31 +05:30
Mihir Kandoi
c76be620a8 Merge pull request #53037 from aerele/fix/qi-naming-rule-issue 2026-03-03 20:38:54 +05:30
Mihir Kandoi
2ce276df5b Merge pull request #53099 from aerele/fix/procurement-report-requestor-invalid 2026-03-03 20:38:10 +05:30
rohitwaghchaure
e1bad10e6c Merge pull request #53123 from rohitwaghchaure/fixed-status-of-serial-no
fix: serial no status for Disassemble entry
2026-03-03 18:53:35 +05:30
Shariq Ansari
93f2a8ad6b Merge pull request #53119 from shariquerik/erpnext-crm-fix 2026-03-03 03:42:52 -08:00
Rohit Waghchaure
81acefa8ad fix: serial no status for Disassemble entry 2026-03-03 17:11:53 +05:30
shariquerik
800810d23d fix: ensure contacts are processed only if present in create_prospect_against_crm_deal 2026-03-03 16:56:00 +05:30
diptanilsaha
a1124449c2 Merge pull request #53110 from diptanilsaha/pr_tt 2026-03-03 15:00:09 +05:30
diptanilsaha
6342e78305 refactor: renamed args to pricing_ctx in set_transaction_type for clarity 2026-03-03 14:42:11 +05:30
Vishnu Priya Baskaran
d2e04750b5 fix: avoid circular dependency (#53109) 2026-03-03 14:32:08 +05:30
diptanilsaha
7ec0354a79 fix(pricing_rule): strict validation of transaction_type 2026-03-03 14:08:49 +05:30
Ravibharathi
9a5b476d9c Merge pull request #51940 from aerele/accounts-receivable-payment-terms-template-filter 2026-03-03 11:27:31 +05:30
diptanilsaha
7ee08c9964 Merge pull request #53104 from frappe/ch-sec 2026-03-03 10:38:15 +05:30
diptanilsaha
b666d9d5c1 chore: update SECURITY.md 2026-03-03 10:36:54 +05:30
kavin-114
bdf4e51da3 fix: populate mr owner and set po owner as fallback 2026-03-02 23:40:40 +05:30
kavin-114
397de1274f fix: pass company in test case using make_quality_inspections 2026-03-02 22:50:21 +05:30
kavin-114
4c39cf2d65 test: add unit test to handle company condition in naming rule 2026-03-02 22:50:11 +05:30
rohitwaghchaure
260fc6a7ce Merge pull request #53093 from rohitwaghchaure/fixed-opening-qty-in-stock-balance
fix: opening qty in stock balance
2026-03-02 20:48:51 +05:30
Mihir Kandoi
c031e345db Merge pull request #52838 from Shllokkk/so-mr-po 2026-03-02 20:41:20 +05:30
Rohit Waghchaure
d7fdab99cb fix: opening qty in stock balance 2026-03-02 17:44:13 +05:30
Shllokkk
35d814c7b3 Merge branch 'develop' into so-mr-po 2026-03-02 16:43:13 +05:30
Shllokkk
91c6475f1c fix: update select query field in patch and code refactor 2026-03-02 16:25:02 +05:30
rohitwaghchaure
735631899f Merge pull request #53087 from aerele/perf/index-reference-purchase-receipt-column
perf: add index on reference_purchase_receipt column
2026-03-02 14:37:08 +05:30
rohitwaghchaure
868a7eb5ca Merge pull request #53082 from rohitwaghchaure/fixed-validate-warehouse-of-sabb
fix: validate warehouse of SABB for draft entry
2026-03-02 14:21:56 +05:30
kavin-114
8c94396ad9 perf: add index on reference_purchase_receipt column 2026-03-02 14:05:40 +05:30
rohitwaghchaure
47b08eaa41 Merge pull request #53072 from aerele/fix/support-#61417
fix(accounts): set posting time to get incoming rate
2026-03-02 13:45:49 +05:30
Rohit Waghchaure
9b8f685c82 fix: validate warehouse of SABB for draft entry 2026-03-02 13:41:20 +05:30
Sudharsanan11
c5b3673a30 fix(accounts): set posting time to get incoming rate 2026-03-02 13:21:25 +05:30
Sudharsanan11
9538a9870c fix(accounts): add transaction time field 2026-03-02 13:21:21 +05:30
Sudharsanan11
6b1aac4aee fix(manufacturing): ignore sales order validation for subassembly item 2026-03-02 12:26:55 +05:30
Diptanil Saha
066442c236 Merge pull request #53079 from diptanilsaha/bvr-validate-dimension 2026-03-02 12:00:37 +05:30
diptanilsaha
79d0708ea7 fix(budget-variance-report): validate 'budget_against' filter 2026-03-02 11:04:35 +05:30
Nishka Gosalia
7b3d85c74e Merge pull request #53047 from trustedcomputer/fix-timesheet-table-read-only 2026-03-02 10:54:44 +05:30
rohitwaghchaure
733528a9f5 Merge pull request #53050 from rohitwaghchaure/fixed-incorrect-voucher-detail-no
fix: voucher detail no in SABB
2026-03-02 09:52:48 +05:30
Pugazhendhi Velu
49d363b174 fix: handle html email template separately in RFQ to avoid jinja context error 2026-03-01 18:21:09 +00:00
Rohit Waghchaure
c37a56ec89 fix: voucher detail no in SABB 2026-03-01 21:54:00 +05:30
Mihir Kandoi
1592229b24 Merge pull request #53067 from mihir-kandoi/correct-precision 2026-03-01 21:21:10 +05:30
Mihir Kandoi
36726b0f7b fix: use the correct precision value in stock reco 2026-03-01 20:48:33 +05:30
Mihir Kandoi
3fad5fb9b9 Merge pull request #53062 from Sanjesh2254/fix/sle-voucher-type-comparison 2026-03-01 20:46:01 +05:30
Sanjesh
cffb59ae73 fix: correct sle voucher_type comparison in get_ref_doctype 2026-03-01 16:31:53 +05:30
Mihir Kandoi
9563af9ae1 Merge pull request #53057 from mihir-kandoi/gh53003 2026-03-01 15:47:12 +05:30
Mihir Kandoi
e68d9af498 Merge pull request #53052 from mihir-kandoi/gh53035 2026-03-01 15:20:55 +05:30
Mihir Kandoi
04127019f9 fix: allow allowed roles to bypass over billing validation 2026-03-01 15:16:38 +05:30
Mihir Kandoi
81fd3c86f4 Merge pull request #53051 from mihir-kandoi/gh53049 2026-03-01 15:09:29 +05:30
Mihir Kandoi
30c3ff2efe fix: use stock qty instead of qty when creating stock entry from MR 2026-03-01 15:04:17 +05:30
Mihir Kandoi
5f12b0db3f fix: use conversion factor when creating stock entry from pick list 2026-03-01 14:53:27 +05:30
trustedcomputer
b9d95711a2 fix: remove read-only property from Sales Invoice Timesheet Table 2026-02-28 18:10:07 -08:00
MochaMind
86ade52dc6 fix: Spanish translations 2026-02-28 22:26:03 +05:30
kavin-114
74def423ed fix(stock): pass company to avoid document naming rule issue in QI
When a Document Naming Rule is configured in QI with a condition based on the company,
the system was not passing the company value properly. As a result, the naming rule
condition was skipped and the document name was generated using the default series.

This fix ensures that the company is passed correctly so that the configured
Document Naming Rule is evaluated and applied as expected.
2026-02-28 13:10:30 +05:30
rohitwaghchaure
061d2e45a9 Merge pull request #52988 from rohitwaghchaure/fixed-same-reposting-picked-by-multiple-jobs
fix: same reposting entry picked by multiple rq jobs
2026-02-28 07:35:44 +05:30
diptanilsaha
df6780bf1a refactor(regional): type annotataions for whitelisted methods 2026-02-28 02:52:03 +05:30
Khushi Rawat
6fc0ea02a1 Merge pull request #53004 from khushi8112/tax-rule-validation-query-builder
refactor: use query builder for Tax Rule validation query
2026-02-27 23:54:03 +05:30
MochaMind
81281d88c7 fix: Serbian (Cyrillic) translations 2026-02-27 22:21:42 +05:30
MochaMind
3dcdde83c7 fix: Serbian (Latin) translations 2026-02-27 22:21:36 +05:30
MochaMind
27381ae77b fix: Swedish translations 2026-02-27 22:21:32 +05:30
MochaMind
63fe68bf28 fix: Spanish translations 2026-02-27 22:21:25 +05:30
Diptanil Saha
6fbdcc3dd1 Merge pull request #53028 from diptanilsaha/qm_ta 2026-02-27 17:06:37 +05:30
ruthra kumar
8397b1ee4b Merge pull request #53027 from ruthra-kumar/remove_frappe_flags_from_dimension_filter
refactor: remove frappe.flags from accounting dimensions filter
2026-02-27 16:54:56 +05:30
Diptanil Saha
a5e4256491 Merge pull request #53026 from diptanilsaha/project_ta 2026-02-27 16:50:36 +05:30
diptanilsaha
eb2f986b11 refactor(quality_management): type annotations for whitelisted methods 2026-02-27 16:48:58 +05:30
Diptanil Saha
5bae06ae96 Merge pull request #53024 from diptanilsaha/maintenance_ta 2026-02-27 16:41:39 +05:30
Diptanil Saha
2712bc8661 Merge pull request #53023 from diptanilsaha/ref_erp_int 2026-02-27 16:40:49 +05:30
ruthra kumar
290f979fd3 refactor: remove frappe.flags from accounting dimensions filter 2026-02-27 16:37:27 +05:30
diptanilsaha
a22ea1de86 refactor(project): type annotations for whitelisted methods 2026-02-27 16:34:04 +05:30
diptanilsaha
13859dfb3c refactor(erpnext_integrations): type annotations for whitelisted methods 2026-02-27 16:19:40 +05:30
diptanilsaha
3078c4a451 fix: get_serial_nos_from_schedule return value 2026-02-27 16:11:45 +05:30
diptanilsaha
73f72bda42 refactor(maintenance): type annotations for whitelisted methods 2026-02-27 16:00:20 +05:30
rohitwaghchaure
aec400c12d Merge pull request #53013 from rohitwaghchaure/revert-stock-reco-issue
fix: old stock reco entries causing issue in the stock balance report
2026-02-27 14:56:07 +05:30
Rohit Waghchaure
0874cbc268 fix: old stock reco entries causing issue in the stock balance report 2026-02-27 14:09:15 +05:30
Mihir Kandoi
048608556c Merge pull request #53012 from thomasantony12/patch-2 2026-02-27 13:53:07 +05:30
Diptanil Saha
397d806f0f Merge pull request #53014 from diptanilsaha/tb_ctb 2026-02-27 12:55:13 +05:30
diptanilsaha
61e01aff67 fix(consolidated-trial=balance): total calculation with show_group_accounts and show_net_values filters 2026-02-27 12:26:20 +05:30
diptanilsaha
560c2511cc feat(consolidated-trial-balance): introduced show_net_values filter 2026-02-27 12:24:47 +05:30
diptanilsaha
f582fdbf71 fix(trial-balance): totals with filter show_group_accounts enabled 2026-02-27 12:21:56 +05:30
ruthra kumar
ffa6280431 Merge pull request #53011 from ruthra-kumar/remove_frappe_flags_from_acc_dimension
refactor: use cache decorator in accounting dimensions
2026-02-27 11:48:06 +05:30
ruthra kumar
565542879e refactor: remove frappe.flags and make use of cache decorator 2026-02-27 11:30:52 +05:30
Thomas antony
b33f06701c feat: UOM query filter for opportunity items
Add UOM query filter based on item code in opportunity form.
2026-02-27 11:11:28 +05:30
MochaMind
1811bb7643 fix: Serbian (Latin) translations 2026-02-26 22:23:42 +05:30
MochaMind
94ea72948e fix: Persian translations 2026-02-26 22:23:37 +05:30
MochaMind
cfda569af1 fix: Swedish translations 2026-02-26 22:23:29 +05:30
MochaMind
4b72ac7d22 fix: Spanish translations 2026-02-26 22:23:21 +05:30
khushi8112
235e3adbcb refactor: use query builder for Tax Rule validation query 2026-02-26 17:18:40 +05:30
Diptanil Saha
c57e452efd Merge pull request #52997 from diptanilsaha/pe-correct-precisions 2026-02-26 14:53:50 +05:30
Rohit Waghchaure
4dfbacfd92 fix: same reposting entry picked by multiple rq jobs 2026-02-26 14:34:39 +05:30
diptanilsaha
d82a0a9455 fix(payment_entry): fix precision for total_allocated_amount and base_total_allocated_amount
Co-authored-by: Ahuahuachi <Ahuahuachi@users.noreply.github.com>
2026-02-26 14:33:50 +05:30
ruthra kumar
c2ed02d4e4 Merge pull request #52630 from mendozal/fix-compute-tax-net-amount-in-js
fix(account): compute tax net_amount in JS controller
2026-02-26 13:47:27 +05:30
Luis Mendoza
b10b205394 fix(accounts): round and convert net_amount to company currency in JS tax controller 2026-02-26 08:09:53 +00:00
Luis Mendoza
485166b668 style: prettier formatting 2026-02-26 08:09:53 +00:00
Luis Mendoza
153ad99f85 fix(accounts): compute tax net_amount in JS controller 2026-02-26 08:09:53 +00:00
ruthra kumar
2d07b346de Merge pull request #52970 from ljain112/fix-missing-type-hint
fix: update type hint for party parameter to allow None in get_party_details and set_taxes functions
2026-02-26 13:32:43 +05:30
ruthra kumar
56732df641 Merge pull request #52188 from aerele/payment-entry-float-precision
fix(payment entry): round unallocated amount
2026-02-26 13:07:33 +05:30
Shllokkk
8c46e06e04 fix: correct get linked payments type hint in bank reconcilliation tool (#52982)
fix: correct type hint for bank reconcilliation tool
2026-02-26 12:41:40 +05:30
ravibharathi656
b0d6751777 fix(payment entry): round unallocated amount 2026-02-26 12:25:44 +05:30
rohitwaghchaure
5d46e1a7b1 Merge pull request #52983 from rohitwaghchaure/fixed-asset-broken-link
fix: broken link of docs in asset onboarding
2026-02-26 11:05:43 +05:30
Rohit Waghchaure
1cdf439e38 fix: broekn link of docs in asset onboarding 2026-02-26 10:47:56 +05:30
Mihir Kandoi
4c374e182d Merge pull request #52952 from nishkagosalia/gh-52947 2026-02-25 20:59:15 +05:30
Diptanil Saha
4c7175fad1 Merge pull request #52973 from ljain112/fix-fiscal-year
fix: ensure cache is cleared on fiscal year update and trash
2026-02-25 20:05:23 +05:30
ljain112
39b0e3522a fix: ensure cache is cleared on fiscal year update and trash 2026-02-25 19:27:52 +05:30
ljain112
f220960d91 fix: update type hint for party parameter to allow None in get_party_details and set_taxes functions 2026-02-25 18:01:24 +05:30
rohitwaghchaure
7ef187b1eb Merge pull request #52967 from rohitwaghchaure/patch-for-existing-records
fix: patch to complete onboarding stpes for existing records
2026-02-25 17:59:17 +05:30
Rohit Waghchaure
d90ec49241 fix: patch to complete onboarding stpes for existing records 2026-02-25 17:23:57 +05:30
rohitwaghchaure
2517369270 Merge pull request #52957 from rohitwaghchaure/fixed-parallel-reposting
fix: reposting creation slow for GL entries
2026-02-25 16:03:26 +05:30
Rohit Waghchaure
d9ac198bad fix: reposting creation slow for GL entries 2026-02-25 14:23:38 +05:30
ruthra kumar
cf09f725ca Merge pull request #52910 from Shllokkk/accounts-type-hints
refactor(accounts): add type hints for whitelisted functions
2026-02-25 13:25:31 +05:30
Nishka Gosalia
a37ffe6e2a Merge pull request #52948 from nishkagosalia/fix-type-hints 2026-02-25 13:12:13 +05:30
Nishka Gosalia
dd0bcf4dbd fix: type hint in stock 2026-02-25 12:54:10 +05:30
Nishka Gosalia
1d4c835843 fix: customer field made mandatory for sales invoice 2026-02-25 12:43:20 +05:30
Shllokkk
61661a159d fix: correct type hints 2026-02-25 12:30:52 +05:30
Mihir Kandoi
1e297a0e5f Merge pull request #52945 from mihir-kandoi/fix-item-link-formatter 2026-02-25 10:25:38 +05:30
Mihir Kandoi
9ef7f05712 fix: item code shows undefined 2026-02-25 10:19:10 +05:30
Mihir Kandoi
10257bbb6e Merge pull request #52942 from mihir-kandoi/gh52359 2026-02-25 10:12:45 +05:30
Mihir Kandoi
bd9e5e97d7 chore: clearer description for internal transfer at arms length 2026-02-25 09:57:13 +05:30
ruthra kumar
bf8b730259 Merge pull request #52940 from frappe/l10n_develop
fix: sync translations from crowdin
2026-02-25 08:49:47 +05:30
MochaMind
cecea63627 fix: Persian translations 2026-02-24 22:28:22 +05:30
MochaMind
d6888a32ae fix: Portuguese, Brazilian translations 2026-02-24 22:28:15 +05:30
MochaMind
1a6a16be5b fix: Swedish translations 2026-02-24 22:28:02 +05:30
MochaMind
dced32088c fix: Spanish translations 2026-02-24 22:27:22 +05:30
Khushi Rawat
bb7fdd59dc Merge pull request #52889 from khushi8112/default-print-format-and-letterhead
feat: default letterhead and print format
2026-02-24 21:21:36 +05:30
Mihir Kandoi
c2a1dbeb67 Merge pull request #52724 from ljain112/fix-subcontracting-ctrl 2026-02-24 20:37:09 +05:30
khushi8112
8a2cb96c2a fix: test cases related to default letterhead change 2026-02-24 19:56:25 +05:30
ruthra kumar
7aa3dbcd86 Merge pull request #51777 from Jatin3128/payment-request-schedules-feat
feat: making payment requests based on payment schedule
2026-02-24 18:38:16 +05:30
khushi8112
570f574758 test: debugging the issue 2026-02-24 18:24:16 +05:30
khushi8112
fbf5529ddd fix: add missing property_type 2026-02-24 18:24:16 +05:30
khushi8112
0ea22f9796 feat: default letterhead and print format 2026-02-24 18:24:16 +05:30
Harsh Patadia
bdcb2c1512 refactor: separate construction of chart related data from get_columns() (#52824)
* fix: avoid hardcoded column slicing for Profit & Loss chart data

* refactor: improve parameter naming and reduce code repetion by using same function get_period_columns()

* refactor: improved parameter naming in get_data() and get_chart_data()
2026-02-24 12:36:47 +00:00
Nishka Gosalia
ddb497acb5 Merge pull request #52834 from nishkagosalia/stock-type-hints 2026-02-24 17:34:04 +05:30
rohitwaghchaure
539d1a4e42 Merge pull request #52930 from rohitwaghchaure/fixed-removed-form-tour-
fix: remove form tour for sales and purchase order
2026-02-24 17:23:33 +05:30
Nishka Gosalia
13995a64b8 refactor(stock): adding type hint for stock module 2026-02-24 17:10:57 +05:30
Rohit Waghchaure
ed7315d78e fix: remove form tour for sales and purchase order 2026-02-24 17:00:02 +05:30
Diptanil Saha
9541cb226d Merge pull request #52029 from ljain112/fix-inclusive-discount 2026-02-24 16:58:12 +05:30
Khushi Rawat
dd910bb4cc Merge pull request #52927 from khushi8112/fix-taxes-and-totals-type-hint
fix: type hint for get_round_off_applicable_accounts
2026-02-24 15:57:47 +05:30
khushi8112
2f3ac06eff fix: type hint for get_round_off_applicable_accounts 2026-02-24 15:36:41 +05:30
Jatin3128
e476dff842 feat(payment request): create payment request as per payment schedules 2026-02-24 07:04:16 +00:00
Jatin3128
60108590b0 feat(payment_request): add option to calculate request amount using payment schedule 2026-02-24 07:04:16 +00:00
Shllokkk
f7ff61be5d refactor(accounts): add type hints for whitelisted functions 2026-02-24 12:30:44 +05:30
rohitwaghchaure
a15e2ecf78 Merge pull request #52839 from rohitwaghchaure/feat-user-onboarding
feat: module onboarding
2026-02-24 12:25:06 +05:30
ruthra kumar
4ba43a538e fix: unhide book_advance_payments_in_separate_party_account check fie… (#52363)
fix: unhide book_advance_payments_in_separate_party_account check field in Payment Entry doctype
2026-02-24 11:51:39 +05:30
Sowmya
d638f3e033 fix(sales-order): update quotation status while cancelling sales order (#52822)
* fix(sales-order): update quotation status while cancelling sales order

* test: validate quotation status

* chore: remove submit
2026-02-24 11:47:48 +05:30
Rohit Waghchaure
792a1a7ab7 feat: module onboarding 2026-02-24 11:45:31 +05:30
rohitwaghchaure
ced0c94e8e fix: sales and purchase modules forms clean-up (#52875) 2026-02-23 23:35:08 +05:30
Rohit Waghchaure
0e356dc2e3 fix: sales and purchase modules forms clean-up 2026-02-23 23:05:05 +05:30
ruthra kumar
99a5886f3a Merge pull request #52896 from aerele/skip-empty-dimensions-exc-gain-loss
fix: skip empty dimension values in exchange gain loss
2026-02-23 21:06:00 +05:30
Mihir Kandoi
e5e164c536 Merge pull request #52905 from frappe/mergify/bp/develop/pr-52544 2026-02-23 20:59:03 +05:30
Mihir Kandoi
bde15cb64b Merge pull request #52417 from Shllokkk/quality-procedure-fix 2026-02-23 20:52:40 +05:30
Imesha Sudasingha
ae3453c84b Merge pull request #52544 from one-highflyer/fix/improve-reserved-serial-no-error-message
fix(stock): improve error message when serial no is reserved via SRE

(cherry picked from commit 71248ff40b)
2026-02-23 15:12:56 +00:00
Mihir Kandoi
a0c8ac524b Merge pull request #52764 from aerele/populate_doctypes_to_be_ignored_table 2026-02-23 20:20:29 +05:30
Diptanil Saha
30e0285ae7 Merge pull request #52891 from diptanilsaha/notifs 2026-02-23 19:37:52 +05:30
Mihir Kandoi
2f635a1021 Merge pull request #52892 from mihir-kandoi/gh52864 2026-02-23 19:06:17 +05:30
Mihir Kandoi
9a253c4f54 Merge pull request #52878 from mihir-kandoi/gh52832 2026-02-23 18:48:45 +05:30
ruthra kumar
68144c5be2 Merge pull request #52812 from aerele/fix-clerance-details
fix: bank account mismatch error on reverse transaction reconciliation
2026-02-23 18:05:55 +05:30
ravibharathi656
7df9d951c6 fix: skip empty dimension values in exchange gain loss 2026-02-23 18:01:10 +05:30
Mihir Kandoi
db00860662 fix: link field displays incorrect value when empty 2026-02-23 17:50:24 +05:30
Mihir Kandoi
a85a0aef52 fix: standalone sales invoice return should not fallback to item master for valuation rate 2026-02-23 17:48:46 +05:30
diptanilsaha
3e87059939 fix: fiscal year notification subject 2026-02-23 17:43:36 +05:30
diptanilsaha
96aa37eff5 fix: material request on receive notification condition 2026-02-23 17:43:08 +05:30
Diptanil Saha
a8b538cc67 Merge pull request #50301 from Abdeali099/fixing-emp-contacts 2026-02-23 17:39:46 +05:30
l0gesh29
60d2f2d304 fix: populate doctypes to be ignored table in validate 2026-02-23 17:20:54 +05:30
Sudharsanan Ashok
8b2a971019 fix(manufacturing): remove delete query of job card & batch and serial no (#52840)
* fix(manufacturing): remove delete query of batch and serial no

* fix(manufacturing): remove delete query of job card

* fix: remove delete function call for work order
2026-02-23 16:58:13 +05:30
Khushi Rawat
9478ccca27 Merge pull request #52861 from khushi8112/print-format-sales-order-purchase-invoice
feat: standard print format for Sales Order and Purchase Invoice
2026-02-23 16:26:48 +05:30
Khushi Rawat
9198bc1b8f Merge pull request #52884 from khushi8112/update-modified-timestamp-in-json
fix: update modified timestamp in json
2026-02-23 16:07:03 +05:30
khushi8112
7b3c10769b fix: update modified timestamp in json 2026-02-23 15:42:57 +05:30
Mihir Kandoi
a2afabab79 Merge pull request #52880 from aerele/fix-support/60562 2026-02-23 15:33:33 +05:30
Khushi Rawat
5e5487faae Merge pull request #52879 from mahsem/typo_recon
fix: typo
2026-02-23 15:23:52 +05:30
mahsem
2b72aab671 fix: typo 2026-02-23 10:34:30 +01:00
Pandiyan37
b7f45e6963 fix(work_order): update returned qty 2026-02-23 15:04:21 +05:30
khushi8112
cbea4493c1 refactor: add translation and fix typo 2026-02-23 14:17:08 +05:30
khushi8112
371efce88a feat: standard print format for Sales Order and Purchase Invoice 2026-02-23 14:17:08 +05:30
Khushi Rawat
f133b96cb4 Merge pull request #52823 from khushi8112/type-hints-for-controllers
refactor: add type hints to controller functions
2026-02-23 14:13:19 +05:30
Mihir Kandoi
853090729a Merge pull request #52871 from mihir-kandoi/gh52856 2026-02-23 13:22:38 +05:30
khushi8112
3677609838 refactor: correct type hint for rm_items 2026-02-23 13:08:26 +05:30
Mihir Kandoi
8e14249335 fix: use stock qty instead of qty when updating transferred qty in WO 2026-02-23 13:02:04 +05:30
khushi8112
e088a596c7 fix: remove unnecessary guest access from get_period_date_ranges 2026-02-23 12:49:46 +05:30
khushi8112
021aa63e24 fix: incorrect type hint 2026-02-23 12:49:46 +05:30
khushi8112
7100d61f3c refactor: add type hints to controller functions 2026-02-23 12:49:46 +05:30
ruthra kumar
3584d2bf23 Merge pull request #52852 from frappe/l10n_develop
fix: sync translations from crowdin
2026-02-23 11:01:57 +05:30
Mihir Kandoi
60e96633c4 Merge pull request #52803 from aerele/fix/support-#60342 2026-02-23 07:30:39 +05:30
Sudharsanan11
cfbdfcf515 test(manufacturing): add test to validate the planned qty 2026-02-23 02:32:40 +05:30
Sudharsanan11
d58171987c test(stock): add test to validate company for receipts and expense accounts 2026-02-23 02:16:21 +05:30
Sudharsanan11
d54d0c25a2 fix: set company based expense account 2026-02-23 02:16:21 +05:30
Sudharsanan11
15dfc08a31 fix(stock): validate company for receipt documents and expense accounts 2026-02-23 02:16:16 +05:30
MochaMind
e2682d2690 fix: Esperanto translations 2026-02-22 22:08:55 +05:30
MochaMind
ad511755ec fix: French translations 2026-02-22 22:08:52 +05:30
MochaMind
d790c60a93 fix: Serbian (Latin) translations 2026-02-22 22:08:49 +05:30
MochaMind
f53ea90113 fix: Norwegian Bokmal translations 2026-02-22 22:08:45 +05:30
MochaMind
a4652e0d5f fix: Bosnian translations 2026-02-22 22:08:42 +05:30
MochaMind
da16cc1727 fix: Burmese translations 2026-02-22 22:08:39 +05:30
MochaMind
a8fe1e24b3 fix: Croatian translations 2026-02-22 22:08:36 +05:30
MochaMind
a24efc6e34 fix: Thai translations 2026-02-22 22:08:33 +05:30
MochaMind
61f6daf55e fix: Persian translations 2026-02-22 22:08:30 +05:30
MochaMind
ddc3842eb7 fix: Indonesian translations 2026-02-22 22:08:26 +05:30
MochaMind
ad7a52e2cb fix: Portuguese, Brazilian translations 2026-02-22 22:08:23 +05:30
MochaMind
589f1246cb fix: Vietnamese translations 2026-02-22 22:08:20 +05:30
MochaMind
cbb3bf6f6a fix: Chinese Simplified translations 2026-02-22 22:08:17 +05:30
MochaMind
3ce05fc0dc fix: Turkish translations 2026-02-22 22:08:13 +05:30
MochaMind
7388b051ed fix: Swedish translations 2026-02-22 22:08:10 +05:30
MochaMind
e4c4f1a0c2 fix: Serbian (Cyrillic) translations 2026-02-22 22:08:06 +05:30
MochaMind
d4f2ea3358 fix: Slovenian translations 2026-02-22 22:08:03 +05:30
MochaMind
d490b2a3c6 fix: Russian translations 2026-02-22 22:08:00 +05:30
MochaMind
8915df90ae fix: Portuguese translations 2026-02-22 22:07:57 +05:30
MochaMind
0ac09df5d9 fix: Polish translations 2026-02-22 22:07:53 +05:30
MochaMind
0f0f8bd64b fix: Dutch translations 2026-02-22 22:07:50 +05:30
MochaMind
3eb0d5fd73 fix: Italian translations 2026-02-22 22:07:46 +05:30
MochaMind
6f77a11522 fix: Hungarian translations 2026-02-22 22:07:43 +05:30
MochaMind
3f2603a34d fix: German translations 2026-02-22 22:07:40 +05:30
MochaMind
48fc867b7b fix: Danish translations 2026-02-22 22:07:37 +05:30
MochaMind
4674b5a207 fix: Czech translations 2026-02-22 22:07:34 +05:30
MochaMind
c2b8b8469d fix: Arabic translations 2026-02-22 22:07:31 +05:30
MochaMind
50daec2972 fix: Spanish translations 2026-02-22 22:07:28 +05:30
MochaMind
63defa40cb chore: update POT file (#52857) 2026-02-22 13:33:52 +01:00
MochaMind
f7830fddeb fix: Vietnamese translations 2026-02-21 21:47:16 +05:30
MochaMind
f65d50b7e4 fix: Swedish translations 2026-02-21 21:47:09 +05:30
MochaMind
dcd47223fa fix: Dutch translations 2026-02-21 21:46:52 +05:30
Diptanil Saha
d6531189f9 Merge pull request #52842 from diptanilsaha/ref-fy
refactor: `Fiscal Year` cleanup
2026-02-21 12:39:14 +05:30
Mihir Kandoi
b49adbd74c Merge pull request #52845 from mihir-kandoi/remove-supplier-invoice-date-validdation 2026-02-21 12:24:48 +05:30
diptanilsaha
4c76786ce4 fix(fiscal_year): Fiscal Year auto-generation and notification 2026-02-21 12:17:09 +05:30
Mihir Kandoi
7cff0ba626 fix: remove supplier invoice date/posting date validation 2026-02-21 12:08:37 +05:30
diptanilsaha
94fb7e11b4 fix(fiscal_year_company): made company field mandatory 2026-02-21 02:02:03 +05:30
diptanilsaha
74ac28fc70 refactor: Fiscal Year DocType cleanup 2026-02-21 01:58:45 +05:30
Shllokkk
d0323dea65 fix: add missing logic to update requested qty on cancel of a material request 2026-02-20 20:29:43 +05:30
Shllokkk
519e115eb2 Merge branch 'develop' into so-mr-po 2026-02-20 20:11:44 +05:30
Shllokkk
1a4d7ad937 fix: correct ordered_qty and requested_qty for Sales Order Item through a patch 2026-02-20 20:09:18 +05:30
Shllokkk
c2f19036f3 fix: correct fields being updated on material request and purchase order creation from sales order 2026-02-20 19:58:23 +05:30
Mihir Kandoi
969f01fca3 Merge pull request #52835 from mihir-kandoi/inconsistent-label-name 2026-02-20 16:58:15 +05:30
Mihir Kandoi
d6e1ca0f10 fix: inconsistent label name between parent and child 2026-02-20 16:40:32 +05:30
Mihir Kandoi
32d09acba9 Merge pull request #52825 from mihir-kandoi/gh52746 2026-02-20 14:23:40 +05:30
Mihir Kandoi
59492cf08e Merge pull request #52821 from mihir-kandoi/gh52770 2026-02-20 14:23:16 +05:30
ruthra kumar
9902473321 Merge pull request #52783 from frappe/l10n_develop
fix: sync translations from crowdin
2026-02-20 14:22:57 +05:30
Mihir Kandoi
ba96d37c11 fix: update items fetches wrong item code 2026-02-20 14:21:26 +05:30
Mihir Kandoi
1352dc79bb fix: sensible insufficient stock message in pick list 2026-02-20 10:29:07 +05:30
Mihir Kandoi
548cdb55d7 Merge pull request #52814 from Shllokkk/selling-type-hints
refactor(selling): add type hints to whitelisted function parameters
2026-02-19 23:07:49 +05:30
Mihir Kandoi
b2afda0462 Merge pull request #52781 from Shllokkk/buying-type-hints
refactor(buying): add type hints for whitelisted function parameters
2026-02-19 23:05:49 +05:30
Mihir Kandoi
866bca70f9 Merge pull request #52811 from nishkagosalia/gh-52373
fix: permission issue for quotation item during update item
2026-02-19 22:57:49 +05:30
Shllokkk
d3911cd7d8 fix: add missing type hints due to failing test case 2026-02-19 22:08:18 +05:30
Raffael Meyer
387fb1b202 feat: add Bank Transaction as Reference Type to Journal Entry Account (#52760)
* feat: add Bank Transaction as Reference Type to Journal Entry Account

* fix: take care of existing property setters

* fix: cancelling Bank Transactions should still be possible

* fix: handle blank options in patch

* fix: hide Reference Due Date for Bank Transaction
2026-02-19 16:20:12 +00:00
MochaMind
0ef15e6364 fix: Serbian (Latin) translations 2026-02-19 21:32:58 +05:30
MochaMind
6e95eebfa4 fix: Thai translations 2026-02-19 21:32:39 +05:30
MochaMind
a4175553fc fix: Swedish translations 2026-02-19 21:32:04 +05:30
MochaMind
b8d7bb2c08 fix: Serbian (Cyrillic) translations 2026-02-19 21:31:59 +05:30
Shllokkk
2504f0fc0c refactor(selling): add type hints to whitelisted function parameters 2026-02-19 20:39:05 +05:30
Mihir Kandoi
732c98b72f fix: typo 2026-02-19 20:28:33 +05:30
Mihir Kandoi
6342e9a3e2 fix: ignore permissions instead of saving parent 2026-02-19 20:27:54 +05:30
ervishnucs
8fe0bf4ba3 fix: check gl account of an associated bank account in bank transaction 2026-02-19 18:39:54 +05:30
Nishka Gosalia
58b8af0fa8 fix: permission issue for quotation item during update item 2026-02-19 17:16:06 +05:30
diptanilsaha
fa5eae08a0 fix(test_purchase_order): validation to create pi from po with terms
Fixed the test to check for error while creating Purchase Invoice from unsubmitted Purchase Order
2026-02-19 14:44:15 +05:30
Mihir Kandoi
4561e3a389 Merge pull request #52804 from marcramser/patch-6
fix(Purchase Receipt): copy project from first row when adding items
2026-02-19 14:01:39 +05:30
Marc Ramser
21423676c9 fix(Purchase Receipt): copy project from first row when adding items
Adds `items_add` method to copy expense_account, cost_center and project from first row to newly added items, matching Purchase Invoice behavior.
2026-02-19 09:25:38 +01:00
Shllokkk
531112e364 refactor(buying): correct broken test case test_make_purchase_invoice_with_terms 2026-02-19 13:22:33 +05:30
Sudharsanan11
4d40c84a31 fix(manufacturing): update status for work order before calculating planned qty 2026-02-19 12:59:11 +05:30
Mihir Kandoi
fc54565d95 Merge pull request #52792 from mihir-kandoi/gh52782
fix: unable to submit subcontracting order if created from material r…
2026-02-19 11:09:17 +05:30
Mihir Kandoi
37323480dd fix: unable to submit subcontracting order if created from material request 2026-02-19 10:50:26 +05:30
Mihir Kandoi
29bc300282 Merge pull request #52794 from mihir-kandoi/sre-read-only
fix: reservation based on field should be read only in SRE
2026-02-19 10:39:36 +05:30
Mihir Kandoi
21452b4c6e fix: reservation based on field should be read only in SRE 2026-02-19 10:18:26 +05:30
Mihir Kandoi
c98e1454fc Merge pull request #52490 from thomasantony12/patch-1
fix: Add handling for Sales Invoice Item quantity field
2026-02-19 09:42:12 +05:30
Mihir Kandoi
1fc2eddf6f fix: add purchase invoice as well 2026-02-19 04:45:20 +05:30
Mihir Kandoi
024c268fe0 Merge pull request #52712 from mihir-kandoi/gh52710
fix: addresses portal
2026-02-19 04:39:35 +05:30
Shllokkk
85c8296548 fix: add missing type hints 2026-02-18 22:37:25 +05:30
MochaMind
b15716f65a fix: Persian translations 2026-02-18 20:27:11 +05:30
MochaMind
a6f5130db1 fix: Portuguese, Brazilian translations 2026-02-18 20:27:07 +05:30
MochaMind
37b9402214 fix: Swedish translations 2026-02-18 20:27:00 +05:30
ruthra kumar
390294a92b Merge pull request #52780 from ruthra-kumar/journal_entry_form_cleanup
refactor: Journal Entry form cleanup
2026-02-18 18:00:46 +05:30
Shllokkk
a6bb44b421 refactor(buying): add type hints for whitelisted function parameters 2026-02-18 17:50:19 +05:30
Khushi Rawat
d3ebcd85b4 Merge pull request #52776 from khushi8112/type-hint-for-projects-module
refactor(projects): add type hints to functions
2026-02-18 17:33:31 +05:30
ruthra kumar
73d5aa35a1 refactor: Journal Entry form cleanup 2026-02-18 17:26:19 +05:30
khushi8112
39d3f4580e refactor(projects): add type hints to functions 2026-02-18 17:13:39 +05:30
mergify[bot]
40391252d5 Merge branch 'develop' into fixing-emp-contacts 2026-02-18 10:47:34 +00:00
Mihir Kandoi
1f62cecdfd Merge pull request #52628 from aerele/fix/support-#59774
fix(manufacturing): set pick list purpose while creating it from work order
2026-02-18 15:14:50 +05:30
Mihir Kandoi
e5f138d004 Merge pull request #52771 from mihir-kandoi/type-hints-manufacturing 2026-02-18 14:16:57 +05:30
Sudharsanan11
23ccc2a8c5 fix(manufacturing): set pick list purpose while creating it from work order 2026-02-18 14:05:42 +05:30
Mihir Kandoi
fad53f6fe4 refactor(manufacturing): add args type hints to functions 2026-02-18 13:58:44 +05:30
Khushi Rawat
7f9066c05a Merge pull request #52734 from khushi8112/type-hint-for-asset-module
refactor(assets): add type annotations in asset module
2026-02-18 12:59:44 +05:30
khushi8112
eb17284711 fix: args must be wrapped under a ctx 2026-02-18 12:35:29 +05:30
MochaMind
e159fc2779 fix: sync translations from crowdin (#52748)
* fix: Portuguese, Brazilian translations

* fix: Persian translations
2026-02-18 12:30:22 +05:30
ruthra kumar
747595082d Merge pull request #52763 from ruthra-kumar/fix_payment_request_bug
fix: better permissions on make payment request
2026-02-18 12:28:06 +05:30
khushi8112
beffc016da refactor: Optimize asset depreciation schedule retrieval with type hints 2026-02-18 12:22:55 +05:30
Mihir Kandoi
f9ec45eda7 Merge pull request #52762 from mihir-kandoi/type-hints-subcontracting
refactor(subcontracting): add arg type hints to functions
2026-02-18 12:17:24 +05:30
Mihir Kandoi
1bc0b40a2e refactor(subcontracting): add arg type hints to functions 2026-02-18 12:01:24 +05:30
ruthra kumar
f36962fc58 fix: better permissions on make payment request 2026-02-18 11:54:53 +05:30
khushi8112
36dcf8b2a5 fix: type hint in make depreciation entry function 2026-02-18 11:33:12 +05:30
Mihir Kandoi
e317ab1479 fix: addresses portal 2026-02-18 11:15:32 +05:30
Diptanil Saha
ea04f55d12 Merge pull request #52759 from diptanilsaha/utils_ta
refactor(utilities): type annotations for whitelisted methods
2026-02-18 01:56:33 +05:30
diptanilsaha
cac6b65dee refactor(utilities): type annotations for whitelisted methods 2026-02-18 01:39:50 +05:30
Diptanil Saha
c6a292f6a9 fix: user permission on reports (#52709) 2026-02-17 21:56:25 +05:30
Mihir Kandoi
e6825476e7 Merge pull request #52750 from mihir-kandoi/st60110
fix: setup fails to set abbr to departments
2026-02-17 21:24:32 +05:30
Mihir Kandoi
54144f72a5 Merge pull request #52743 from Mutantpenguin/patch-3
bug: fix comparison regarding `None` values
2026-02-17 21:21:25 +05:30
Mihir Kandoi
debe868950 fix: setup fails to set abbr to departments 2026-02-17 21:07:30 +05:30
khushi8112
773cc80ed4 fix: incorrect type hint 2026-02-17 17:51:27 +05:30
Markus Lobedann
3fd5a0f100 fix: bug with comparison regarding None values and empty string
In their default state, the fields can be `None`. When a user enters something and deletes it afterwards, the fields contain an empty string.

This fixes the comparison.
2026-02-17 12:46:30 +01:00
rohitwaghchaure
83f5c03494 Merge pull request #52729 from rohitwaghchaure/feat-negative-batch-report
feat: Negative Batch report
2026-02-17 16:33:28 +05:30
Mihir Kandoi
eb0fa4d26d Merge pull request #52677 from mihir-kandoi/standalone-credit-debit-note
fix: standalone credit/debit notes should not fetch any serial or bat…
2026-02-17 16:09:47 +05:30
Mihir Kandoi
4f4bd98943 Merge pull request #52733 from mihir-kandoi/allow-sequence-id-edit
fix: allow sequence ID edit in BOM if routing is not set
2026-02-17 16:05:33 +05:30
Rohit Waghchaure
34edbed00b feat: Negative Batch report 2026-02-17 16:05:13 +05:30
khushi8112
84773a3a32 refactor(assets): add type annotations in asset module 2026-02-17 15:56:24 +05:30
Mihir Kandoi
08529964b4 fix: allow sequence id edit in BOM if routing is not set 2026-02-17 15:39:52 +05:30
ruthra kumar
2c011bb9ad Merge pull request #52679 from AarDG10/fix-acc-timeline
fix: log changes made to accounts settings
2026-02-17 13:55:24 +05:30
ruthra kumar
ee9ce0dee8 Merge pull request #52648 from Abdeali099/Abdeali/fix-typo
fix: correct typos in marketing campaign custom fields function
2026-02-17 13:41:40 +05:30
ljain112
1d3d09f48c refactor: use postprocess in mapped_doc to update items in subcontracting controller 2026-02-17 13:16:41 +05:30
ruthra kumar
e8ef6f1afb Merge pull request #52654 from Jarif-Junaeed/develop
fix: resolve POS crash and correct is_return typo in TransactionBase
2026-02-17 13:10:29 +05:30
Mihir Kandoi
a374d0b3e7 Merge pull request #52720 from Shllokkk/pricing-rule-fix
fix: wrong display_depends_on condition for item group and brand chil…
2026-02-17 12:59:22 +05:30
Shllokkk
de2843d9f1 fix: wrong display_depends_on condition for item group and brand child tables 2026-02-17 12:28:15 +05:30
Vishnu Priya Baskaran
ae0be7f6ce Merge pull request #52649 from aerele/fix-sales-funnel-layout
fix: ensure layout has Bootstrap row and column
2026-02-17 12:25:15 +05:30
Mihir Kandoi
7860673855 Merge pull request #52716 from mihir-kandoi/gh52551
fix: do not allow plant floor company and warehouse to be updated
2026-02-17 12:11:53 +05:30
Mihir Kandoi
fd72132743 fix: do not allow plant floor company and warehouse to be updated 2026-02-17 11:54:12 +05:30
Mihir Kandoi
1eb9f5b803 Merge pull request #52713 from mihir-kandoi/st60127
fix: production plan status
2026-02-17 11:23:17 +05:30
Mihir Kandoi
b3e6b304e4 fix: production plan status 2026-02-17 10:43:47 +05:30
Diptanil Saha
c8c54a42e2 Merge pull request #52706 from diptanilsaha/wsb-pp
fix(selling-workspace-sidebar): changed order of pos profile
2026-02-17 00:40:33 +05:30
diptanilsaha
72f4fd08ee fix(selling-workspace-sidebar): changed order of pos profile 2026-02-17 00:23:01 +05:30
rohitwaghchaure
4defb104db Merge pull request #52620 from Shllokkk/production-plan-fix
fix: prevent rows from being added to sub_assembly_items and mr_items
2026-02-17 00:11:31 +05:30
Mihir Kandoi
48cafc8e57 Merge pull request #52626 from aerele/fix/support-#59888
fix(manufacturing): add sales order fields in subassembly child table
2026-02-16 23:41:16 +05:30
rohitwaghchaure
8adf8aed41 Merge pull request #52699 from rohitwaghchaure/fixed-negative-stock-validation-sle
fix: consider sle for negative stock validation
2026-02-16 23:25:52 +05:30
Sudharsanan11
341dc4be7a test(manufacturing): add test to validate the sales order references for sub assembly items 2026-02-16 23:21:35 +05:30
Sudharsanan11
0f2ed28ab7 fix(manufacturing): set sales order references in subassembly child table 2026-02-16 23:21:35 +05:30
Sudharsanan11
c2282eaf08 fix(manufacturing): add sales order fields in subassembly child table 2026-02-16 23:21:35 +05:30
Rohit Waghchaure
38f35acffe fix: consider sle for negative stock validation 2026-02-16 23:02:15 +05:30
Shllokkk
25f979a825 fix: prevent rows from being added to sub_assembly_items and mr_items 2026-02-16 22:33:36 +05:30
rohitwaghchaure
afa14e6cd4 Merge pull request #52678 from mihir-kandoi/sales-purchase-invoice-update-stock-cleanup
chore: do not show stock details if update stock is disabled
2026-02-16 22:31:25 +05:30
Diptanil Saha
802507d321 Merge pull request #52692 from frappe/l10n_develop
fix: sync translations from crowdin
2026-02-16 21:16:11 +05:30
rohitwaghchaure
12ff8e6279 Merge pull request #52691 from rohitwaghchaure/fixed-cancel-sabb-for-lcv
fix: cancel SABB if SLE cancelled from LCV
2026-02-16 21:11:38 +05:30
Soham Kulkarni
219cf6bc57 fix(pos_invoice): add correct depends on condition (#52689)
* fix(pos_invoice): add correct depends on condition

* fix: show field in sales order

* refactor: eval condition
2026-02-16 15:36:30 +00:00
MochaMind
a7ec72b25c fix: Esperanto translations 2026-02-16 20:22:05 +05:30
MochaMind
4bd3d4d36c fix: Serbian (Latin) translations 2026-02-16 20:22:01 +05:30
MochaMind
d425270c88 fix: Norwegian Bokmal translations 2026-02-16 20:21:57 +05:30
MochaMind
f8c8c1cc2d fix: Bosnian translations 2026-02-16 20:21:53 +05:30
MochaMind
cc02c411b6 fix: Burmese translations 2026-02-16 20:21:49 +05:30
MochaMind
65dd7d4851 fix: Croatian translations 2026-02-16 20:21:45 +05:30
MochaMind
47b80cbc2b fix: Thai translations 2026-02-16 20:21:39 +05:30
MochaMind
20d2317f61 fix: Persian translations 2026-02-16 20:21:35 +05:30
MochaMind
d2a4b8bfd8 fix: Indonesian translations 2026-02-16 20:21:30 +05:30
MochaMind
2b26d859c3 fix: Portuguese, Brazilian translations 2026-02-16 20:21:26 +05:30
MochaMind
3516cda350 fix: Vietnamese translations 2026-02-16 20:21:22 +05:30
MochaMind
4d51d0d351 fix: Chinese Simplified translations 2026-02-16 20:21:19 +05:30
MochaMind
303b7a117c fix: Turkish translations 2026-02-16 20:21:15 +05:30
MochaMind
83006a3ed1 fix: Swedish translations 2026-02-16 20:21:11 +05:30
MochaMind
b3e9fa2142 fix: Serbian (Cyrillic) translations 2026-02-16 20:21:07 +05:30
MochaMind
45d7a398b6 fix: Slovenian translations 2026-02-16 20:21:01 +05:30
MochaMind
ffb88c30b9 fix: Russian translations 2026-02-16 20:20:58 +05:30
MochaMind
40fd9a8008 fix: Portuguese translations 2026-02-16 20:20:53 +05:30
MochaMind
eb357a2805 fix: Polish translations 2026-02-16 20:20:48 +05:30
MochaMind
fd2cadd431 fix: Dutch translations 2026-02-16 20:20:44 +05:30
MochaMind
e66bbe4eec fix: Italian translations 2026-02-16 20:20:40 +05:30
Rohit Waghchaure
f23a49a25e fix: cancel SABB if SLE cancelled from LCV 2026-02-16 20:20:39 +05:30
MochaMind
1c749c01cd fix: Hungarian translations 2026-02-16 20:20:37 +05:30
MochaMind
f95ff68c10 fix: German translations 2026-02-16 20:20:30 +05:30
MochaMind
5815a92273 fix: Danish translations 2026-02-16 20:20:26 +05:30
MochaMind
bb8d84e65e fix: Czech translations 2026-02-16 20:20:23 +05:30
MochaMind
c28020a1bd fix: Arabic translations 2026-02-16 20:20:19 +05:30
MochaMind
95b0ec70cb fix: Spanish translations 2026-02-16 20:20:15 +05:30
MochaMind
1c0f2e1fba fix: French translations 2026-02-16 20:20:11 +05:30
Khushi Rawat
83338675f9 Merge pull request #52331 from aerele/fix-validate-asset-category-accounts
fix(asset): validate depreciation when category has active depreciable assets
2026-02-16 17:45:18 +05:30
Khushi Rawat
a6f808702f Merge pull request #52688 from khushi8112/toggle-reference-doc
fix: toggle reference doc on asset_type change
2026-02-16 17:38:39 +05:30
khushi8112
8e71060931 fix: toggle reference doc on asset_type change 2026-02-16 17:28:51 +05:30
rohitwaghchaure
659112842e Merge pull request #52681 from rohitwaghchaure/fixed-better-validation-message-for-batch
fix: better validation for negative batch
2026-02-16 15:15:54 +05:30
Rohit Waghchaure
a8636e4f59 fix: better validation for negative batch 2026-02-16 13:06:11 +05:30
AarDG10
45febbabd7 fix: log changes made to accounts settings 2026-02-15 23:18:49 +05:30
Mihir Kandoi
cdc62e7327 chore: do not show serial batch selector if not needed 2026-02-15 21:09:04 +05:30
Mihir Kandoi
4499e974a0 chore: do not show stock details if update stock is disabled 2026-02-15 20:54:44 +05:30
Mihir Kandoi
2017edca88 fix: standalone credit/debit notes should not fetch any serial or batch by default 2026-02-15 20:29:57 +05:30
MochaMind
894771029c chore: update POT file (#52673) 2026-02-15 14:00:17 +01:00
Mihir Kandoi
bdf909e94e Merge pull request #52670 from mihir-kandoi/st59534
fix: total weight does not update when updating items
2026-02-15 14:14:11 +05:30
Mihir Kandoi
63323a2611 fix: total weight does not update when updating items 2026-02-15 13:58:32 +05:30
Mihir Kandoi
36393e447e Merge pull request #52658 from aerele/fix-filter-for-update-items
fix: allow non-stock items while updating items
2026-02-15 12:57:39 +05:30
Diptanil Saha
fe01206a73 Merge pull request #52666 from frappe/l10n_develop
fix: sync translations from crowdin
2026-02-15 11:29:26 +05:30
MochaMind
e766bb0cd7 fix: Burmese translations 2026-02-14 19:11:32 +05:30
MochaMind
53e3222935 fix: Persian translations 2026-02-14 19:11:29 +05:30
Jarif Junaeed
47353fbceb fix: resolve POS crash and correct is_return typo in TransactionBase 2026-02-14 14:08:57 +06:00
ruthra kumar
a23c2f2166 Merge pull request #52643 from aerele/quotation-lost-reason-detail
fix: consider table multiselect in delete transaction
2026-02-13 14:07:07 +05:30
ervishnucs
07db5941aa fix: allow non-stock items while updating items 2026-02-13 11:58:54 +05:30
ruthra kumar
dd9e1814b0 Merge pull request #52651 from frappe/l10n_develop
fix: sync translations from crowdin
2026-02-13 10:14:33 +05:30
MochaMind
0e262de96f fix: Spanish translations 2026-02-12 19:09:18 +05:30
Abdeali Chharchhoda
6b7fed7f59 fix: correct typos in marketing campaign custom fields function 2026-02-12 15:41:14 +05:30
ruthra kumar
7ba2ed6f3f Merge pull request #52644 from ruthra-kumar/use_query_builder_for_profitability_analysis
refactor: use query builder for profitability analysis
2026-02-12 14:24:04 +05:30
ruthra kumar
5e34325604 refactor: use query builder for profitability analysis 2026-02-12 14:08:27 +05:30
SowmyaArunachalam
9bb60405e7 fix: removed lost reason detail 2026-02-12 13:26:47 +05:30
SowmyaArunachalam
be3d2422a7 fix: consider table multiselect in delete transaction 2026-02-12 13:22:17 +05:30
Navin-S-R
bd9d9c3cc7 fix: validate depreciation accounts exist for all companies with depreciable assets 2026-02-12 13:08:19 +05:30
ruthra kumar
1218c0f518 Merge pull request #52640 from ruthra-kumar/use_query_builder_for_sales_person_commision
refactor: use query builder for sales person commission summary
2026-02-12 12:36:43 +05:30
ruthra kumar
7105e3fb69 refactor: use query builder for sales person commission summary 2026-02-12 12:19:51 +05:30
ruthra kumar
ae985c00e9 Merge pull request #52571 from belee-asp/fix/report-letterhead-hardcode
fix(stock): remove hardcoded letter_head from report
2026-02-12 11:48:58 +05:30
ruthra kumar
803c09a4ae Merge pull request #52619 from aerele/feat-show-formatted-currency-on-preview
feat: show formatted currency symbol on ledger preview
2026-02-12 11:28:21 +05:30
Navin-S-R
1b288682e8 chore: resolve conflicts 2026-02-12 11:13:46 +05:30
Navin-S-R
7d14833856 fix: handle duplicate acctount row for the same company 2026-02-12 11:13:46 +05:30
Navin-S-R
2c6054d51a fix(asset category): validate depreciation accounts when category has active depreciable assets 2026-02-12 11:13:46 +05:30
Navin-S-R
464e1929db fix(asset): validate depreciation accounts for depreciable asset upon creation 2026-02-12 11:13:46 +05:30
ruthra kumar
33d0e9559a Merge pull request #52507 from ruthra-kumar/toggle_for_utm_parameters
refactor: toggle for UTM parameters
2026-02-12 10:55:45 +05:30
ruthra kumar
20af546b26 refactor: better description on toggle 2026-02-12 10:38:50 +05:30
Diptanil Saha
7e99a1f4b0 Merge pull request #52625 from frappe/l10n_develop 2026-02-11 19:15:44 +05:30
MochaMind
2adde497c8 fix: Serbian (Latin) translations 2026-02-11 18:59:56 +05:30
MochaMind
97d6a35edc fix: Serbian (Cyrillic) translations 2026-02-11 18:59:32 +05:30
Navin-S-R
5c8cb1e7ec feat: show formatted currency symbol on ledger preview 2026-02-11 17:29:20 +05:30
ruthra kumar
d1217dfedd Merge pull request #52593 from ljain112/tds-report-columns
refactor: update labels for tax withholding reports columns to improve clarity
2026-02-10 18:12:09 +05:30
ruthra kumar
4ddb3c7a6c Merge pull request #52602 from frappe/l10n_develop2
fix: sync translations from crowdin
2026-02-10 18:11:22 +05:30
ruthra kumar
3ed7627ec3 Merge pull request #52017 from aerele/fix-gross-profit-return-period
fix(gross-profit): handle returns outside the given sale period
2026-02-10 18:10:30 +05:30
MochaMind
1b5d5c5be8 fix: Swedish translations 2026-02-10 17:54:26 +05:30
MochaMind
4946a13d45 fix: Italian translations 2026-02-10 17:54:05 +05:30
ruthra kumar
578783af8e refactor: utm analytics section delivery note 2026-02-10 14:36:55 +05:30
ruthra kumar
223d560b32 refactor: utm section in opportunity 2026-02-10 14:36:55 +05:30
ruthra kumar
71e8941285 refactor: utm analytics section in lead 2026-02-10 14:36:52 +05:30
ruthra kumar
7de0ca164d refactor: utm analytics section in pos profile 2026-02-10 14:24:59 +05:30
ruthra kumar
0bafa347a5 refactor: utm section pos invoice 2026-02-10 14:23:28 +05:30
ljain112
2cfdcc1af4 refactor: update labels for tax withholding reports columns to improve clarity 2026-02-10 12:54:46 +05:30
ruthra kumar
678d261f40 refactor: utm section in sales invoice 2026-02-10 11:59:17 +05:30
ruthra kumar
c00bf7df6a refactor: utm section in sales order 2026-02-10 11:56:32 +05:30
Navin-S-R
047b278791 fix(gross-profit): handle item group filters 2026-02-10 11:47:35 +05:30
ruthra kumar
aaf5f923b0 refactor: hide UTM parameters 2026-02-10 11:40:12 +05:30
Navin-S-R
fdfa7bc963 test: fix test assertions to use index-based totals 2026-02-10 11:29:58 +05:30
Navin-S-R
3ab978ab46 test: validate sales person wise gross profit 2026-02-10 11:29:58 +05:30
Navin-S-R
4da3d43013 test: validate return invoice profit and profit percentage 2026-02-10 11:29:58 +05:30
Navin-S-R
51709f032f fix: handle gross profit and percentage for return invoices 2026-02-10 11:29:58 +05:30
Navin-S-R
67d8223f73 fix(gross-profit): handle returns outside sale period 2026-02-10 11:29:57 +05:30
El-Shafei H.
da07f84e44 fix: Added a missing option to the currency field (#52528) 2026-02-10 02:14:06 +05:30
V Shankar
23a73c9cdb fix(map_current_doc): prevent mutation of query args in get_query (#52202) 2026-02-10 00:16:59 +05:30
Mihir Kandoi
7b197cbe1c Merge pull request #52527 from aerele/fix/support-59242
fix(stock): correct warehouse mapping for material issue
2026-02-09 21:05:56 +05:30
Roxxane
99cd29d88f fix(stock): remove hardcoded letter_head from report
The 'Incorrect Serial and Batch Bundle' report had a hardcoded
letter_head value of 'Test', preventing users from deleting a
Letter Head named 'Test' due to link check.

Standard reports should not reference specific Letter Head names.

Fixes #52569
2026-02-09 22:25:11 +08:00
Akhil Narang
0cf4c2ded5 Merge pull request #52559 from akhilnarang/drop-db-query-usage
refactor: drop usages of db_query
2026-02-09 18:52:04 +05:30
Diptanil Saha
271a982cb4 Merge pull request #52562 from frappe/l10n_develop2
fix: sync translations from crowdin
2026-02-09 17:49:10 +05:30
Pandiyan37
f22b9e297b fix(stock): inward stock for pick list test record 2026-02-09 17:45:50 +05:30
MochaMind
61d0863239 fix: Esperanto translations 2026-02-09 17:30:45 +05:30
MochaMind
803cc7c653 fix: Serbian (Latin) translations 2026-02-09 17:30:37 +05:30
MochaMind
b4341df44c fix: Norwegian Bokmal translations 2026-02-09 17:30:31 +05:30
MochaMind
4a77a8b840 fix: Bosnian translations 2026-02-09 17:30:27 +05:30
MochaMind
680117e97e fix: Burmese translations 2026-02-09 17:30:22 +05:30
MochaMind
4bab80faaa fix: Croatian translations 2026-02-09 17:30:11 +05:30
MochaMind
74a1707e3f fix: Thai translations 2026-02-09 17:30:01 +05:30
MochaMind
f145331ae5 fix: Persian translations 2026-02-09 17:29:57 +05:30
MochaMind
be5df6b2b7 fix: Indonesian translations 2026-02-09 17:29:53 +05:30
MochaMind
14a5ff2b54 fix: Portuguese, Brazilian translations 2026-02-09 17:29:48 +05:30
MochaMind
f585ddea71 fix: Vietnamese translations 2026-02-09 17:29:44 +05:30
MochaMind
958ba61f66 fix: Chinese Simplified translations 2026-02-09 17:29:40 +05:30
MochaMind
f7b18159a6 fix: Turkish translations 2026-02-09 17:29:37 +05:30
MochaMind
ae759481e1 fix: Swedish translations 2026-02-09 17:29:33 +05:30
MochaMind
c2bafcf83b fix: Serbian (Cyrillic) translations 2026-02-09 17:29:28 +05:30
MochaMind
04b5e1b3a0 fix: Slovenian translations 2026-02-09 17:29:24 +05:30
MochaMind
3207a9e642 fix: Russian translations 2026-02-09 17:29:19 +05:30
MochaMind
67e8286a57 fix: Portuguese translations 2026-02-09 17:29:14 +05:30
MochaMind
7c0c41f3de fix: Polish translations 2026-02-09 17:29:09 +05:30
MochaMind
c5da02dbd0 fix: Dutch translations 2026-02-09 17:29:05 +05:30
MochaMind
ee1ae88ecd fix: Italian translations 2026-02-09 17:29:01 +05:30
MochaMind
e7b8c257a3 fix: Hungarian translations 2026-02-09 17:28:57 +05:30
MochaMind
93078bcf0c fix: German translations 2026-02-09 17:28:54 +05:30
MochaMind
566072a17f fix: Danish translations 2026-02-09 17:28:50 +05:30
MochaMind
31191096ad fix: Czech translations 2026-02-09 17:28:46 +05:30
MochaMind
0975222e3a fix: Arabic translations 2026-02-09 17:28:42 +05:30
MochaMind
576279fa92 fix: Spanish translations 2026-02-09 17:28:38 +05:30
MochaMind
21eec60924 fix: French translations 2026-02-09 17:28:34 +05:30
Poojashree T R
e98b68c38f fix: validate asset movement transaction date (#52340)
* fix: validate asset transaction date

* fix: validate asset transaction date

* fix: add translation in validate_transaction_date

* test: test_movement_transaction_date

* fix: to ensure test reliability
2026-02-09 17:20:42 +05:30
Poojashree T R
155f6afb48 fix: set asset status as fully depreciated (#52309)
* fix: set asset status as fully depreciated

* test: test_is_fully_depreciated_asset_status

* fix: remove unused condition
2026-02-09 17:01:40 +05:30
Akhil Narang
1e45195ef9 refactor: drop usages of db_query
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
2026-02-09 16:53:02 +05:30
rohitwaghchaure
3a2e4ff5e5 Merge pull request #52550 from rohitwaghchaure/feat-allow-negative-stock-for-batch
feat: allow negative stock for the batch item
2026-02-09 16:19:29 +05:30
Mihir Kandoi
b2a0d6559f Merge pull request #52501 from aerele/quotation-ordered-status-regression
fix(quotation): ignore zero ordered_qty
2026-02-09 16:08:22 +05:30
Pratik Badhe
22123dd955 fix: email campaign timeout issue (#51994)
* fix: email campaign timeout issue

* refactor: email campaign backend logic

* refactor: use sendmail instead of manually batching
2026-02-09 16:07:46 +05:30
Rohit Waghchaure
376ab0e346 feat: allow negative stock for the batch item 2026-02-09 15:04:19 +05:30
Pandiyan37
da0322e994 test(stock): add test to check from warehouse for issue type 2026-02-09 13:06:27 +05:30
rohitwaghchaure
479b44c610 Merge pull request #52516 from aerele/fix/support-#59057
fix(stock): ignore pos reserved batches for stock levels
2026-02-09 11:57:43 +05:30
Pandiyan37
a34e8c99cd fix(stock): set source warehouse for issue type 2026-02-09 10:58:50 +05:30
Mihir Kandoi
b8e1758dcf Merge pull request #52538 from mihir-kandoi/revert-gh52220
revert: "fix: allow sales invoice to be renamed"
2026-02-09 10:29:47 +05:30
Mihir Kandoi
2660907ac8 revert: "fix: allow sales invoice to be renamed"
This reverts commit 95fdbe55f9.
2026-02-09 10:13:23 +05:30
rohitwaghchaure
9f8c08fd18 Merge pull request #52534 from rohitwaghchaure/form-cleanup-supplier-form
refactor: supplier form cleanup
2026-02-09 09:36:32 +05:30
Sudharsanan11
47ac67f7a2 test(stock): add test to ignore pos reserved batches for stock levels 2026-02-08 23:28:06 +05:30
Jannat Patel
0cf9915698 fix: sync translations from crowdin (#52449)
* fix: French translations

* fix: Spanish translations

* fix: Arabic translations

* fix: Czech translations

* fix: Danish translations

* fix: German translations

* fix: Hungarian translations

* fix: Italian translations

* fix: Dutch translations

* fix: Polish translations

* fix: Portuguese translations

* fix: Russian translations

* fix: Slovenian translations

* fix: Serbian (Cyrillic) translations

* fix: Swedish translations

* fix: Turkish translations

* fix: Chinese Simplified translations

* fix: Vietnamese translations

* fix: Portuguese, Brazilian translations

* fix: Indonesian translations

* fix: Persian translations

* fix: Thai translations

* fix: Croatian translations

* fix: Burmese translations

* fix: Bosnian translations

* fix: Norwegian Bokmal translations

* fix: Serbian (Latin) translations

* fix: Esperanto translations

* fix: Serbian (Cyrillic) translations

* fix: Swedish translations

* fix: Croatian translations

* fix: Bosnian translations

* fix: Serbian (Latin) translations

* fix: Persian translations

* fix: Italian translations
2026-02-08 17:50:31 +01:00
MochaMind
dd1027f165 chore: update POT file (#52531) 2026-02-08 17:49:49 +01:00
Rohit Waghchaure
bd521d9089 refactor: supplier form cleanup 2026-02-08 20:37:57 +05:30
rohitwaghchaure
ebce5d2635 Merge pull request #52508 from rohitwaghchaure/form-cleanup-stock-manufacturing
refactor: form cleanup for stock and manufacturing doctypes
2026-02-08 20:21:46 +05:30
Sudharsanan11
277ba9cb79 fix(stock): ignore pos reserved batches for stock levels 2026-02-08 10:05:07 +05:30
Mihir Kandoi
86389b46e2 Merge pull request #52497 from aerele/fix/support-#59190
fix: add is_group filter for supplier_group and warehouse fields
2026-02-07 21:40:41 +05:30
Rohit Waghchaure
bf20ecca60 refactor: form cleanup for stock and manufacturing doctypes 2026-02-06 19:27:35 +05:30
ruthra kumar
8c960620cc Merge pull request #52504 from ruthra-kumar/ux_refactor_sales_order
refactor: form cleanup for sales order
2026-02-06 17:27:07 +05:30
Khushi Rawat
7be1c885cd Merge pull request #52393 from khushi8112/form-cleanup-assets
refactor: assets module form cleanup
2026-02-06 17:23:06 +05:30
ruthra kumar
3957df6511 Merge pull request #52495 from ruthra-kumar/ux_refactor_for_quotation
refactor: form cleanup for quotation
2026-02-06 17:13:19 +05:30
Sudharsanan11
a9829f5f7b fix(stock): add is group filter for warehouse fields 2026-02-06 17:05:24 +05:30
ruthra kumar
9db3fa1b65 refactor: form cleanup for quotation
1. use standard names for dimension section
2026-02-06 16:48:11 +05:30
ruthra kumar
93d1716eb5 refactor: form cleanup for sales order 2026-02-06 16:24:53 +05:30
khushi8112
4f59d580c4 chore: linters check 2026-02-06 16:22:44 +05:30
khushi8112
d1b81b96a5 fix: implement coderabbit suggested changes 2026-02-06 16:19:40 +05:30
khushi8112
9f7bfc0e36 refactor: asset repair UI changes 2026-02-06 16:19:40 +05:30
khushi8112
eb7932ed73 refactor: asset capitalization form cleanup 2026-02-06 16:19:40 +05:30
khushi8112
e8e8d233ab fix: test cases fixes related to new select box change 2026-02-06 16:19:40 +05:30
khushi8112
f2f509234b fix: test case 2026-02-06 16:19:40 +05:30
khushi8112
e90a3b5a56 fix(UI): improve asset action buttons group 2026-02-06 16:19:40 +05:30
khushi8112
c36fa5bdb6 fix: patch to migrate checkbox data into select 2026-02-06 16:19:40 +05:30
khushi8112
f7b9221324 refactor: use selectbox instead of checkboxes for asset type 2026-02-06 16:19:40 +05:30
khushi8112
4e7794bfc3 fix(UI): reposition fields for better UX 2026-02-06 16:19:40 +05:30
Mihir Kandoi
71ad323cb6 Merge pull request #52499 from nishkagosalia/fix-test-case 2026-02-06 15:58:48 +05:30
ravibharathi656
32ea37035e fix(quotation): ignore zero ordered_qty 2026-02-06 15:50:01 +05:30
Nishka Gosalia
81dceceb4f fix: test case 2026-02-06 15:42:15 +05:30
rohitwaghchaure
d02a85df50 Merge pull request #52494 from rohitwaghchaure/fixed-remove-lable-currency
fix: remove currency label for rounding adjustment
2026-02-06 13:30:24 +05:30
Rohit Waghchaure
000af2bc96 fix: remove currency label for rounding adjustment 2026-02-06 13:29:23 +05:30
Sudharsanan11
cfdc554a19 fix(buying): add supplier group link filters in field level 2026-02-06 13:12:04 +05:30
Khushi Rawat
6e744bcaa7 Merge pull request #52491 from khushi8112/asset-field-toggle-issue
fix: apply composite asset logic only in draft
2026-02-06 13:04:31 +05:30
khushi8112
ee501e884a fix: apply composite asset logic only in draft 2026-02-06 12:58:42 +05:30
Thomas antony
edfcaee99b fix: Add handling for Sales Invoice Item quantity field
Add handling for Sales Invoice Item quantity field
2026-02-06 12:48:15 +05:30
Mihir Kandoi
31ec3af164 Merge pull request #52219 from nishkagosalia/st-56775
fix: enabling skip delivery option for order type maintenance
2026-02-06 12:09:28 +05:30
Mihir Kandoi
70053fea8e Merge pull request #52000 from nishkagosalia/gh-51010
fix: Added validation for quality inspection in job card
2026-02-06 12:07:41 +05:30
Mihir Kandoi
ac9232d888 Merge pull request #52475 from mihir-kandoi/gh52358-2
fix: do not show update stock flag unneccessarily
2026-02-06 11:57:11 +05:30
Nishka Gosalia
1a22e3cb61 fix: enabling skip delivery option for order type maintenance 2026-02-06 11:53:09 +05:30
Diptanil Saha
57d14f695f refactor: global defaults (#52481)
* fix(global_defaults): set disable rounded total on POS Profiles

* fix: removed re-setting fields on toggle functions

* fix: removed property setter for print_hide
2026-02-06 10:18:07 +05:30
rohitwaghchaure
1415472674 Merge pull request #52476 from rohitwaghchaure/fixed-stock-reservation-for-fg
fix: stock reservation created against job card
2026-02-06 04:34:59 +05:30
Rohit Waghchaure
dca2cfd009 fix: stock reservation created against job card 2026-02-05 22:06:54 +05:30
Mihir Kandoi
5fb5b7b30e fix: do not show update stock flag unneccessarily 2026-02-05 20:45:46 +05:30
rohitwaghchaure
a632d5f313 Merge pull request #52469 from rohitwaghchaure/form-cleanup-master-doctypes
refactor: form cleanup for master doctypes related to stock module
2026-02-05 19:46:18 +05:30
rohitwaghchaure
4136ac26c4 Merge pull request #52467 from rohitwaghchaure/fixed-github-52466
fix: operation status and bom validation
2026-02-05 19:45:39 +05:30
Diptanil Saha
a67b548f44 fix(pos_profile): allow_partial_payment field description (#52470)
fix(pos_profile): allow_partial_payment field description
2026-02-05 13:07:40 +00:00
Nikhil Kothari
ef44528ba5 feat(accounts): expand Journal Entry Template to support dimensions and party (#51621)
* feat(accounts): expand Journal Entry Template to support dimensions and party

* fix: do not update standard row values
2026-02-05 18:24:26 +05:30
Rohit Waghchaure
6c49d5dc7d refactor: form cleanup for master doctypes related to stock module 2026-02-05 18:17:37 +05:30
Rohit Waghchaure
95baf953a8 fix: operation status and bom validation 2026-02-05 18:14:35 +05:30
rohitwaghchaure
c86e316670 Merge pull request #52459 from rohitwaghchaure/fixed-stock-reco-balance-value
fix: stock balance report issue
2026-02-05 14:49:22 +05:30
Rohit Waghchaure
7e584dd84a fix: stock balance report issue 2026-02-05 14:32:48 +05:30
Jatin3128
cb4e6ad825 Merge pull request #52438 from Jatin3128/fix-chart-label
fix(balance sheet): removed the extra labels from the chart
2026-02-05 13:23:55 +05:30
Deepesh Garg
4584893542 Merge pull request #52453 from deepeshgarg007/erpnext_form_cleanups_v1
fix: Currency fields label (Sales Invoice)
2026-02-05 13:10:53 +05:30
Mihir Kandoi
6b0fe32180 Merge pull request #52452 from mihir-kandoi/gh52451
fix: process loss error incorrectly thrown even when semi FG BOM does…
2026-02-05 12:57:22 +05:30
Deepesh Garg
e08fe10f31 fix: Currency fields label 2026-02-05 12:54:48 +05:30
Mihir Kandoi
99ddc36c26 fix: process loss error incorrectly thrown even when semi FG BOM does not have any process loss 2026-02-05 12:42:12 +05:30
Smit Vora
26854d8d42 Merge pull request #52360 from vorasmit/fix-calc-balance
fix: correctly calculate running balances for financial report
2026-02-05 12:23:09 +05:30
Diptanil Saha
1c5e36017c feat(pos): allow warehouse change configuration (#52437) 2026-02-05 12:00:42 +05:30
Mihir Kandoi
611f7d07c2 Merge pull request #52445 from mihir-kandoi/gh52444
fix: item code is tuple with operation id
2026-02-05 11:57:55 +05:30
Mihir Kandoi
481deee4b2 fix: item code is tuple with operation id 2026-02-05 11:42:52 +05:30
Deepesh Garg
116ea657cd Merge pull request #52399 from deepeshgarg007/erpnext_form_cleanups
refactor: Better organizing of the fields in various doctypes
2026-02-05 11:27:39 +05:30
Mihir Kandoi
e9befff0cb Merge pull request #52416 from aerele/fix/inventory-dimension-attribute
fix(stock): update target field attribute
2026-02-05 10:23:31 +05:30
Deepesh Garg
1770d92ea6 Merge branch 'develop' into erpnext_form_cleanups 2026-02-05 09:50:22 +05:30
Jatin3128
a64b5f2c5d fix(balance sheet): removed the extra labels from the chart 2026-02-05 05:02:46 +05:30
Diptanil Saha
812d7de7f7 fix: pos profile form cleanup (#52436) 2026-02-05 04:21:14 +05:30
Pandiyan37
21d0ee8db1 test(stock): testcase for different inventory dimension 2026-02-04 22:33:39 +05:30
Pandiyan37
14818b8311 Merge branch 'develop' of https://github.com/frappe/erpnext into fix/inventory-dimension-attribute 2026-02-04 22:09:09 +05:30
Mihir Kandoi
c9dbd58e7f Merge pull request #52427 from archielister/develop 2026-02-04 20:02:33 +05:30
rohitwaghchaure
e63ff83f24 Merge pull request #52422 from rohitwaghchaure/fixed-not-able-to-complete-jc
fix: not able to complete job card
2026-02-04 18:42:56 +05:30
rohitwaghchaure
d3e269a3d3 Merge pull request #52392 from rohitwaghchaure/form-cleanup-buying
refactor: Cleanup buying module related doctypes
2026-02-04 18:13:00 +05:30
ruthra kumar
b33a805702 Merge pull request #51990 from ruthra-kumar/security_fixes
refactor: use https over http while saving website link
2026-02-04 18:01:55 +05:30
Deepesh Garg
efc4c900f3 chore: linting issues 2026-02-04 17:55:06 +05:30
archielister
e4df0a393a fix for obtaining bom_no 2026-02-04 12:20:43 +00:00
ruthra kumar
a3bd73b9db Merge pull request #52419 from ruthra-kumar/plug_payment_request_vulnerability
fix: enfore permission on make_payment_request
2026-02-04 17:44:27 +05:30
ruthra kumar
8db29b0a81 refactor: patch partner_website for old data 2026-02-04 17:43:31 +05:30
ruthra kumar
8cf31548f2 refactor: scrub http and use https in sales partner 2026-02-04 17:43:29 +05:30
Deepesh Garg
3a91f6793f refactor: Add advance settings 2026-02-04 17:35:27 +05:30
Shllokkk
ced2f33a63 fix: correct link filter for processes child table procedure field 2026-02-04 17:22:19 +05:30
Deepesh Garg
954173d629 refactor: Add advance settings 2026-02-04 17:16:40 +05:30
Rohit Waghchaure
f3ea1863ae refactor: Cleanup buying module forms 2026-02-04 17:07:25 +05:30
Rohit Waghchaure
175fe9279c fix: not able to complete job card 2026-02-04 17:04:13 +05:30
Mihir Kandoi
3b45485351 Merge pull request #51773 from aerele/fix/production-analytics-report 2026-02-04 16:57:23 +05:30
ruthra kumar
b755ca12ca fix: enfore permission on make_payment_request 2026-02-04 16:54:51 +05:30
Deepesh Garg
0687e683f8 refactor: Add advance settings 2026-02-04 16:50:58 +05:30
Pandiyan37
7e08154217 fix(stock): update target field attribute 2026-02-04 15:19:56 +05:30
Deepesh Garg
a734f90920 fix: Add totals and base total section 2026-02-04 12:41:19 +05:30
Sudharsanan11
27091e5168 fix(manufacturing): fix chart period keys 2026-02-04 12:18:03 +05:30
Mihir Kandoi
3f8af9b3c9 Merge pull request #52383 from mihir-kandoi/fix-rate-comparison-3 2026-02-04 12:17:53 +05:30
Mihir Kandoi
e8d1e9d946 fix: return None instead of 0 if valuation rate is falsy 2026-02-04 12:02:14 +05:30
Deepesh Garg
6d64a6a1d1 Merge branch 'develop' of https://github.com/frappe/erpnext into erpnext_form_cleanups 2026-02-04 11:57:05 +05:30
Deepesh Garg
2f2e16fc33 refactor: Better organizing of the fields in various doctypes 2026-02-04 11:18:09 +05:30
Sudharsanan11
16f09141da fix(manufacturing): handle None value for actual_end_date 2026-02-04 11:14:47 +05:30
Diptanil Saha
036f64013d fix: remove customer_pos_id reference (#52396) 2026-02-04 10:47:56 +05:30
Jatin3128
51cfd6119f Merge pull request #51745 from elshafei-developer/fix-translate-report-Gross-Profit
fix(gross profit report): translate column Sales Invoice
2026-02-04 04:43:23 +05:30
ruthra kumar
b719a6c767 fix: group item wise tax details by tax row (#51898) 2026-02-03 21:08:01 +05:30
NaviN
6fde0a6261 fix: merge taxes in purchase receipt when get items from multiple purchase invoices (#51422)
* fix: merge taxes in purchase receipt when get items from multiple purchase invoices

* fix: make merge tax configurable

* chore: follow standard merge taxes method

* chore: follow standard merge taxes method
2026-02-03 21:03:14 +05:30
Mihir Kandoi
671726a3e4 Merge pull request #52259 from aerele/fix/support-#58787 2026-02-03 20:24:43 +05:30
Mihir Kandoi
f1b4fe12a2 fix: rate comparison in stock reco 2026-02-03 20:21:51 +05:30
Mihir Kandoi
4c4d8859f6 Merge pull request #52374 from aerele/fix/fetch-batch-valuation-rate-in-stock-recon 2026-02-03 20:17:12 +05:30
rohitwaghchaure
a3ba0a18c4 Merge pull request #52278 from aerele/feat/add-company-field-bin
feat(stock): add company field to Bin with migration patch
2026-02-03 20:10:44 +05:30
ruthra kumar
2e51e7fb78 Merge pull request #51651 from aerele/ppr-skip-ref-detail
fix: correct exchange gain loss in ppr
2026-02-03 20:05:19 +05:30
ruthra kumar
6387aebed8 Merge pull request #51558 from ili-ad/fix/postgres-customer-autoname
fix(postgres): avoid UNSIGNED cast in customer autoname
2026-02-03 19:48:49 +05:30
ruthra kumar
b478f0da89 Merge pull request #52314 from aerele/fix/journal-entry-exchange-rate-type
fix(journal-entry): normalize exchange rate to float
2026-02-03 19:47:00 +05:30
rohitwaghchaure
6d41e35242 Merge pull request #52375 from frappe/mergify/bp/develop/pr-52369
fix: zero valuation rate if returning from different warehouse (backport #52369)
2026-02-03 19:29:08 +05:30
Rohit Waghchaure
eb2119e292 fix: zero valuation rate if returning from different warehouse
(cherry picked from commit 28929df0e8)
2026-02-03 13:41:15 +00:00
kavin-114
c5df570262 fix(stock): fetch batch wise valuation rate in get_items 2026-02-03 18:36:18 +05:30
Smit Vora
12f8bb2937 test: further tests for query builder 2026-02-03 18:11:34 +05:30
ruthra kumar
7fd6278c18 Merge pull request #52320 from mendozal/fix-sales-tax-template-sidebar-link
fix: correct Sales Tax Template sidebar link to proper DocType
2026-02-03 17:35:14 +05:30
ruthra kumar
c90f77af44 Merge pull request #52279 from aerele/profit-loss-chart-xaxis
fix(profit and loss statement): exclude non period columns
2026-02-03 17:29:18 +05:30
rohitwaghchaure
c29bcbfed8 Merge pull request #52160 from aerele/support-57403
fix(stock): remove is_return condition on pos batch qty calculation
2026-02-03 16:41:09 +05:30
Smit Vora
a29710dc07 test: correct error message 2026-02-03 16:32:26 +05:30
Smit Vora
f45a5a63a7 test: revert original pcv setting 2026-02-03 16:30:05 +05:30
ruthra kumar
82aa32f74e Merge pull request #51997 from aerele/feat/add-partially-billed-status
Add partially billed status indicator
2026-02-03 16:28:24 +05:30
Shllokkk
5793322c30 fix: unhide book_advance_payments_in_separate_party_account check field in Payment Entry doctype 2026-02-03 16:26:35 +05:30
Smit Vora
61d8308e81 test: add tests for query builder 2026-02-03 16:18:27 +05:30
Smit Vora
b41c1858a3 fix: Period Closing Voucher doesn't exist for GL Entry 2026-02-03 16:03:14 +05:30
rohitwaghchaure
4a7d606099 Merge pull request #52338 from rohitwaghchaure/fixed-negative-stock-error-for-purchase-return
fix: negative stock for purchase return
2026-02-03 15:54:39 +05:30
Sudharsanan11
de8f8ef9f4 fix(stock): include subcontracting order qty while calculating the bin qty 2026-02-03 15:37:15 +05:30
Shllokkk
93a4b9d49f Merge pull request #52346 from Shllokkk/ux-fixes
fix: move company field to first position in sales invoice, purchase …
2026-02-03 15:34:33 +05:30
ruthra kumar
9160aeb30e Merge pull request #52339 from sokumon/rename-icons
chore: rename icons
2026-02-03 15:30:20 +05:30
Smit Vora
ee2f8d8ebc fix: correctly calculate running balances for financial report 2026-02-03 15:21:38 +05:30
sokumon
2d312bcfe8 chore: rename icons 2026-02-03 15:21:17 +05:30
ruthra kumar
3ee11307f1 Merge pull request #52345 from ruthra-kumar/ignore_svg
ci: skip svg
2026-02-03 15:19:40 +05:30
Shllokkk
8e9365eb3b fix: move company field to first position in sales invoice, purchase invoice, sales order, purchase order and journal entry 2026-02-03 15:04:39 +05:30
Rohit Waghchaure
77893933a2 fix: negative stock for purchase return 2026-02-03 15:02:59 +05:30
ruthra kumar
e565d2283e ci: skip svg 2026-02-03 14:59:52 +05:30
ruthra kumar
ff2f015d67 Merge pull request #52280 from aerele/fix/support-#57038
fix(stock): ignore packing slip while cancelling the sales invoice
2026-02-03 13:53:51 +05:30
Dharanidharan2813
be0040ddc7 fix(journal-entry): normalize exchange rate to float 2026-02-03 12:51:53 +05:30
kavin-114
12ec997027 test: add unit test case for pos reserved with return qty 2026-02-03 12:43:09 +05:30
Dharanidharan2813
7767000ccf feat(delivery-note): add status indicator when document is partially billed 2026-02-03 12:29:55 +05:30
Praveenkumar26-S
d54259c99c feat(stock): add company field to Bin with migration patch 2026-02-03 11:41:20 +05:30
Nishka Gosalia
46b4cf3add fix: Added validation for quality inspection in job card 2026-02-03 11:38:36 +05:30
ruthra kumar
42f2abf5ae Merge pull request #51655 from aerele/project-gross-margin-credit-note
fix: include credit notes in project gross margin calculation
2026-02-03 11:37:09 +05:30
Mihir Kandoi
dbbeca6308 Merge pull request #52184 from aerele/fix/support-#58126 2026-02-03 09:09:21 +05:30
Luis Mendoza
06a7c85c93 fix: correct Sales Tax Template sidebar link to proper DocType 2026-02-02 20:53:47 +00:00
Dharanidharan S
7a1c4a5ded fix: avoid duplicate taxes and charges rows in payment entry (#52178) 2026-02-03 00:56:02 +05:30
Apriliansyah Idris
6cc802147b fix: duplicate account number (Indonesia COA) (#52080) 2026-02-03 00:22:43 +05:30
Sudharsanan11
4d9412181c test(subcontracting): add test for consumed_qty calculation with similar finished goods 2026-02-02 22:52:30 +05:30
Sudharsanan11
0d372a62a1 fix(subcontracting): include item bom in supplied items grouping key 2026-02-02 22:51:33 +05:30
rohitwaghchaure
62a98a0504 Merge pull request #52303 from rohitwaghchaure/fixed-batch-selector-v16
fix: batch selector not working if Use Legacy (Client side) Reactivity disabled
2026-02-02 22:01:25 +05:30
Mihir Kandoi
135a433018 Merge pull request #52246 from mihir-kandoi/st58765 2026-02-02 15:14:22 +00:00
Mihir Kandoi
af6a1eb189 Merge pull request #52304 from mihir-kandoi/customer-contact-quotation 2026-02-02 20:15:59 +05:30
Mihir Kandoi
a8068b89ed Merge pull request #52281 from aerele/fix/stock-bal-report-opening 2026-02-02 20:06:53 +05:30
Mihir Kandoi
75b2c2c83d fix: populate contact fields when creating quotation from customer 2026-02-02 19:59:53 +05:30
Rohit Waghchaure
d1cba1073f fix: batch selector not working if Use Legacy (Client side) Reactivity disabled 2026-02-02 19:54:27 +05:30
Mihir Kandoi
c176af4fe3 Merge pull request #52286 from mihir-kandoi/reset-incoming-rate 2026-02-02 19:47:04 +05:30
Mihir Kandoi
2d6b43fd54 fix: reset incoming rate in selling controller if there are changes in item 2026-02-02 14:08:27 +05:30
ruthra kumar
20ce96e358 Merge pull request #52200 from Tom-1508/fix-journal-print-date
fix(accounts): correct date in Journal Auditing Voucher print format
2026-02-02 12:51:02 +05:30
Tamal Majumdar
43e2495df8 fix: journal auditing voucher print date to use posting_date 2026-02-02 12:42:38 +05:30
ruthra kumar
0e4c37bb6d Merge pull request #51692 from aerele/fix/payment-reconciliation-spelling
fix: correct spelling of Payment Reconciliation in Accounting
2026-02-02 12:39:33 +05:30
kavin-114
f3eb6c7078 fix(stock): add stock recon opening stock condition 2026-02-02 12:36:09 +05:30
nivithamerlin
35e53d28df fix: correct spelling of Payment Reconciliation in Accounting 2026-02-02 12:24:36 +05:30
ravibharathi656
6180e5eb53 fix(profit and loss statement): exclude non period columns 2026-02-02 12:05:45 +05:30
Sudharsanan11
c58887b44a fix(stock): ignore packing slip while cancelling the sales invoice 2026-02-02 11:59:42 +05:30
Mihir Kandoi
c7fd855092 Merge pull request #52274 from mihir-kandoi/over-order-quotation-test 2026-02-02 10:07:20 +05:30
Mihir Kandoi
53e58f6678 test: over ordering of quotation items 2026-02-02 09:51:09 +05:30
MochaMind
5ea7a91fe1 chore: update POT file (#52265) 2026-02-01 15:07:23 +01:00
Mihir Kandoi
200cefa0a5 Merge pull request #52253 from mihir-kandoi/st58854 2026-01-31 20:03:17 +05:30
Mihir Kandoi
f5d74b4572 Merge pull request #52252 from mihir-kandoi/item-report-view-blank 2026-01-31 19:50:51 +05:30
Mihir Kandoi
d9998a977c fix: make item name editable in RFQ 2026-01-31 19:47:51 +05:30
Mihir Kandoi
b24ae5e9a2 fix: better fix for aac39b2671 2026-01-31 19:42:27 +05:30
rohitwaghchaure
54d96bb761 Merge pull request #52232 from rohitwaghchaure/fixed-validate-repack-stock-entry
fix: validation when more than one FG items in repack stock entry
2026-01-31 12:46:02 +05:30
Rohit Waghchaure
6423ce2fa7 fix: validation when more than one FG items in repack stock entry 2026-01-31 12:30:18 +05:30
Mihir Kandoi
6066fef1c9 Merge pull request #52231 from UmakanthKaspa/fix/report-view-item-code 2026-01-30 22:15:40 +05:30
UmakanthKaspa
b20f57321f fix: item code not showing in report view 2026-01-30 14:13:34 +00:00
Mihir Kandoi
33b6fd408f Merge pull request #52222 from mihir-kandoi/st56549 2026-01-30 19:30:05 +05:30
Mihir Kandoi
36f1e3572c fix: test cases 2026-01-30 19:16:39 +05:30
Mihir Kandoi
4cc306d2d8 fix: validate over ordering of quotation 2026-01-30 18:36:24 +05:30
Mihir Kandoi
3e9a3c1185 Merge pull request #52226 from mihir-kandoi/failing-tests 2026-01-30 17:47:44 +05:30
Mihir Kandoi
d3f44a425c fix: failing test cases 2026-01-30 17:30:50 +05:30
Mihir Kandoi
058be326f0 Merge pull request #51433 from mihir-kandoi/naming-rule-based-on-posting-date 2026-01-30 17:10:50 +05:30
Mihir Kandoi
46b742cd4c Merge pull request #52223 from mihir-kandoi/gh52220 2026-01-30 17:02:10 +05:30
Mihir Kandoi
95fdbe55f9 fix: allow sales invoice to be renamed 2026-01-30 16:46:41 +05:30
Mihir Kandoi
fc8647d1da Merge pull request #52209 from mihir-kandoi/fix-precision-pr 2026-01-30 12:05:16 +05:30
Mihir Kandoi
f82f1da706 Merge pull request #52213 from mihir-kandoi/st58513 2026-01-30 11:58:53 +05:30
Mihir Kandoi
6e17ccf499 fix: hide close button on WO if WO is completed 2026-01-30 11:56:38 +05:30
Mihir Kandoi
40bfd08866 Merge pull request #52210 from mihir-kandoi/gh52189 2026-01-30 11:46:49 +05:30
Mihir Kandoi
89f6f0f46f fix(barcode): failing request when item has both batch and serial 2026-01-30 11:37:21 +05:30
Mihir Kandoi
838d245215 fix: add precision to rejected batch no qty calculation 2026-01-30 10:19:35 +05:30
Dany Robert
b565dd3da8 fix: missing depr_series causing error on jv creation (#52085) 2026-01-29 23:10:04 +05:30
Mihir Kandoi
e1b6ec340c Merge pull request #52201 from mihir-kandoi/gh52199 2026-01-29 21:42:29 +05:30
Mihir Kandoi
c38f884095 fix: hide item_wise_tax_details table from print 2026-01-29 21:25:39 +05:30
rohitwaghchaure
464560a949 Merge pull request #52190 from rohitwaghchaure/fixed-lead-time-calculation-for-fg
fix: lead time calculation for FG item
2026-01-29 17:58:44 +05:30
rohitwaghchaure
f7f2e73f79 Add Landed Cost Voucher Amount in Internal Purchase Receipt (#52158)
* fix(stock): set incoming_rate with lcv rate for internal purchase

* test: add unit test to check internal purchase with lcv
2026-01-29 17:32:13 +05:30
Rohit Waghchaure
646688c291 fix: lead time calculation for FG item 2026-01-29 17:30:22 +05:30
kavin-114
dd4fd89ef8 test: add unit test to check internal purchase with lcv 2026-01-29 16:48:39 +05:30
kavin-114
f0dccc3cd7 fix(stock): set incoming_rate with lcv rate for internal purchase 2026-01-29 16:48:32 +05:30
Mihir Kandoi
91b1df49a4 Merge pull request #52181 from mihir-kandoi/st58663 2026-01-29 15:30:29 +05:30
Mihir Kandoi
7f6f39f5e7 fix: js error if user does not have write permission for date field 2026-01-29 15:28:55 +05:30
Jacob Salvi
cdcf3fa593 refactor: new accounting icons (#52173)
* chore: new icons share-management

* chore: new accounting icons

* chore: trigger CI

---------

Co-authored-by: Soham Kulkarni <77533095+sokumon@users.noreply.github.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2026-01-29 14:42:47 +05:30
jacob-salvi
728c678cf9 chore: new accounting icons 2026-01-29 11:47:57 +05:30
Diptanil Saha
e11ba21b42 fix(demo): removed toolbar eventlistener (#52171) 2026-01-29 05:57:12 +00:00
Mihir Kandoi
1a4ecba742 Merge pull request #52166 from mihir-kandoi/gh52113 2026-01-29 10:49:53 +05:30
Mihir Kandoi
4e19c7e8bd fix: production plan not considering planning datetime when creating WO 2026-01-29 10:16:06 +05:30
Aarol D'Souza
578b06e027 Merge pull request #52092 from AarDG10/fix-email-render-rfq
fix(RFQ): render email templates for preview and sending
2026-01-29 09:06:51 +05:30
kavin-114
2c19c1fd06 fix(stock): remove is_return condition on pos batch qty calculation 2026-01-29 02:17:20 +05:30
Soham Kulkarni
3d65db2ac3 feat: clear demo data from desktop screen (#52128) 2026-01-28 17:13:22 +05:30
rohitwaghchaure
fabc26bb69 Merge pull request #52007 from aerele/fix/set-zero-rate-for-expired-batch
Fix: Set Zero Rate for Standalone Credit Note with Expired Batch
2026-01-28 16:12:37 +05:30
mahsem
27226b1d82 chore: delete swedish 2024 chart of accounts template (#52032) 2026-01-28 15:09:48 +05:30
Mihir Kandoi
0dc804f9b4 Merge pull request #51961 from aerele/sales-order-project-dimensions 2026-01-27 21:53:39 +05:30
Mihir Kandoi
3192f3f011 Merge pull request #52084 from harrishragavan/fix/shipment-field-validation 2026-01-27 21:26:42 +05:30
harrishragavan
3c6eb9a531 fix(shipment): user contact validation to use full name 2026-01-27 20:52:26 +05:30
Soham Kulkarni
8dae178728 Merge pull request #52119 from sokumon/blue-icons 2026-01-27 20:43:51 +05:30
sokumon
6f9cd8c261 chore: change color of icons in accounting folders 2026-01-27 20:15:00 +05:30
ruthra kumar
d6189b8101 Merge pull request #51894 from ruthra-kumar/refactor_accounting_workspace
refactor: accounting workspace
2026-01-27 18:45:10 +05:30
Nikhil Kothari
48f4a44fb5 feat(accounts): retain filters when switching between financial statements (#51668) 2026-01-27 18:38:37 +05:30
ruthra kumar
f0332c4dc7 refactor: reuse icon for invoicing 2026-01-27 18:30:03 +05:30
NaviN
ec41f1b0f5 fix(asset capitalization): update total_asset_cost on asset capitalisation submission (#52077)
fix(asset capitalization): update total_asset_cost on asset capitalization submission
2026-01-27 18:04:04 +05:30
madelyngamble2
7e9647f3f0 fix: unable to split asset from capitalization (#52020)
* fix: Allow split asset from capitalized composite asset (fixes #52016)

* test: Add test case for splitting asset created via capitalization (fixes #52016)

* docs: Add docstring to before_submit method

* fix: Remove unused variable and fix UTF-8 encoding in asset files

* fix: Remove UTF-8 BOM from asset.py to fix linting

* fix: Fix test_split_asset_created_via_capitalization test parameters

* fix: Remove unused import create_item

* chore: remove unnecessary comments

Removed validation comments for composite asset capitalization in before_submit method.

---------

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2026-01-27 17:46:58 +05:30
ruthra kumar
fb9656b975 refactor: rename Accounts to Accounting 2026-01-27 17:23:40 +05:30
Mihir Kandoi
1b6fe8498d Merge pull request #52106 from mihir-kandoi/gh34977 2026-01-27 16:27:14 +05:30
Mihir Kandoi
5eeebbde7f test: fix tests 2026-01-27 16:10:44 +05:30
ruthra kumar
f7abf9c1da refactor: link payments dashboard to sidebar 2026-01-27 15:52:57 +05:30
ruthra kumar
99406ccc15 refactor: payments dashboard 2026-01-27 15:52:57 +05:30
ruthra kumar
1295d7aa30 refactor: shed duplicates from invoicing sidebar 2026-01-27 15:52:57 +05:30
ruthra kumar
5a680d5037 refactor: reorder accounts setup sidebar 2026-01-27 15:52:57 +05:30
ruthra kumar
7528d42187 refactor: introduce setup icon and reorder 2026-01-27 15:52:57 +05:30
ruthra kumar
cbdc945287 refactor: payments sidebar and icon 2026-01-27 15:52:57 +05:30
ruthra kumar
faf0dcb102 chore: rename accounting to invoicing 2026-01-27 15:52:57 +05:30
ruthra kumar
5e02b4009e chore: remove accounting icon 2026-01-27 15:52:57 +05:30
ruthra kumar
8125f9035c refactor: invoicing icon 2026-01-27 15:52:57 +05:30
Vishnu Priya Baskaran
efa3973b77 fix: check the payment ledger entry has the dimension (#51823)
* fix: check the payment ledger entry has the dimension

* fix: add project in payment ledger entry
2026-01-27 15:45:33 +05:30
Mihir Kandoi
71371b0ba5 fix: show everything else besides other party specific item 2026-01-27 15:43:58 +05:30
SowmyaArunachalam
543b6e51c0 fix: handle parent level project change 2026-01-27 15:37:14 +05:30
kavin-114
3460a7efb5 test(credit-note): add unit test for zero valuation rate on expired batch 2026-01-27 15:13:46 +05:30
kavin-114
e78c750b4e fix(credit-note): set incoming rate as zero for expired batch 2026-01-27 15:13:34 +05:30
Dharanidharan S
d82c92a237 fix(accounts): correct base grand total and rounded total mismatch (#51739) 2026-01-27 14:08:16 +05:30
Mihir Kandoi
826cf66af8 Merge pull request #52088 from mihir-kandoi/gh51577 2026-01-27 12:19:58 +05:30
Mihir Kandoi
b49c679a50 fix: show message if image is removed from item description 2026-01-27 12:04:49 +05:30
NaviN
5f05714e9d fix(payment entry): update currency symbol (#51956) 2026-01-27 12:01:11 +05:30
AarDG10
37cdae2f34 ci: minor text correction 2026-01-27 11:59:18 +05:30
SowmyaArunachalam
3b27f49d79 chore: use frappe.model.set_value 2026-01-27 11:36:51 +05:30
AarDG10
525b3960e1 fix(RFQ): render email templates for preview and sending 2026-01-27 11:35:50 +05:30
kavin-114
04cdf88715 feat(credit-note): add checkbox to set valuation rate as zero for expired batch 2026-01-27 11:04:36 +05:30
V Shankar
f8f626975f fix(journal-entry): prevent submit failure due to double background queuing (#52083) 2026-01-27 11:00:38 +05:30
rohitwaghchaure
31c536e33f Merge pull request #52062 from rohitwaghchaure/fixed-github-52028
fix: not able to complete the job card
2026-01-26 23:14:20 +05:30
Mihir Kandoi
c1fef8269a Merge pull request #52064 from Shankarv19bcr/customer-auto-name-fix 2026-01-26 15:16:21 +05:30
Shankarv19bcr
e5ba0e6401 fix: strip whitespace in customer_name 2026-01-26 14:37:55 +05:30
Rohit Waghchaure
696ea68f86 fix: not able to complete the job card 2026-01-26 11:35:48 +05:30
MochaMind
71d00f5290 chore: update POT file (#52057) 2026-01-25 20:37:19 +01:00
Henning Wendtland
0fb37ad792 feat(Transaction Deletion Record): Editable "DocTypes To Delete" List with CSV import/export (#50592)
* feat: add editable DocTypes To Delete list with import/export

Add user control over transaction deletion with reviewable and reusable deletion templates.

- New "DocTypes To Delete" table allows users to review and customize what will be deleted before submission
- Import/Export CSV templates for reusability across environments
- Company field rule: only filter by company if field is specifically named "company", otherwise delete all records
- Child tables (istable=1) automatically excluded from selection
- "Remove Zero Counts" helper button to clean up list
- Backward compatible with existing deletion records

* refactor: improve Transaction Deletion Record code quality

- Remove unnecessary chatty comments from AI-generated code
- Add concise docstrings to all new methods
- Remove redundant @frappe.whitelist() decorators from internal methods
- Improve CSV import validation (header check, child table filtering)
- Add better error feedback with consolidated skip messages
- Reorder form fields: To Delete list now appears before Excluded list
- Add conditional visibility for Summary table (legacy records only)
- Improve architectural clarity: single API entry point per feature

Technical improvements:
- export_to_delete_template_method and import_to_delete_template_method
  are now internal helpers without whitelist decorators
- CSV import now validates format and provides detailed skip reasons
- Summary table only shows for submitted records without To Delete list
- Maintains backward compatibility for existing deletion records

* fix: field order

* test: fix broken tests and add new ones

* fix: adapt create_transaction_deletion_request

* test: fix assertRaises trigger

* fix: conditionally execute Transaction Deletion pre-tasks based on selected DocTypes

* refactor: replace boolean task flags with status fields

* fix: remove UI comment

* fix: don't allow virtual doctype selection and improve protected Doctype List

* fix: replace outdated frappe.db.sql by frappe.qb

* feat: add support for multiple company fields

* fix: autofill comapny field, add docstrings, filter for company_field

* fix: add edge case handling for update_naming_series and add tests for prefix extraction

* fix: use redis for running deletion validation, check per doctype instead of company
2026-01-25 14:20:28 +05:30
rohitwaghchaure
88069779b2 Merge pull request #52050 from mahsem/swedish_address_template
fix: swedish_address_template
2026-01-25 10:51:39 +05:30
rohitwaghchaure
c5a4164a6b Merge pull request #52043 from rohitwaghchaure/fixed-uom-not-fetching-in-bom
fix: UOM of item not fetching in BOM
2026-01-25 10:44:28 +05:30
mahsem
334e8ada30 fix: swedish_address_template 2026-01-24 23:41:30 +01:00
Rohit Waghchaure
ba8eadda52 fix: UOM of item not fetching in BOM 2026-01-24 14:15:56 +05:30
Smit Vora
297a2ea259 Merge pull request #51691 from Abdeali099/do-not-warn-filter-missing 2026-01-24 12:37:45 +05:30
Smit Vora
e129e1438e Merge pull request #51670 from Abdeali099/fix-bank-quik-entry-erroe 2026-01-24 12:36:46 +05:30
HALFWARE
e810cd8440 fix: update country_wise_tax.json for Algerian Taxes (#51878)
* Algeria chart of accounts

Algeria chart of accounts

* Update Algeria Chart Of Account

* Algeria chart of account

* Algeria Chart of Account

Algeria Chart of Account

* Modify Algeria tax entries in country_wise_tax.json

Updated tax rates and account names for Algeria.

* Rename account for Algeria tax from VAT to TVA

Rename account for Algeria tax from VAT to TVA
2026-01-24 12:02:39 +05:30
El-Shafei H.
50b3396064 fix: Ensure paid_amount is always numeric before calling allocate_amount_to_references (#50935)
fix: ensure paid_amount is not null in allocate_party_amount_against_ref_docs
2026-01-24 11:47:38 +05:30
Abdeali Chharchhoda
9322095786 refactor: use console.error for error logging in Plaid integration 2026-01-24 11:34:34 +05:30
Abdeali Chharchhoda
8a1b8259bd fix: handle undefined bank_transaction_mapping in quick entry 2026-01-24 11:33:42 +05:30
Abdeali Chharchhoda
d905f78984 refactor: not warn when filter field is missing in FS reports 2026-01-24 11:21:17 +05:30
rohitwaghchaure
7250ee4429 Merge pull request #52024 from rohitwaghchaure/fixed-support-56966
fix: Bin reserved qty for production for extra material transfer
2026-01-23 21:13:20 +05:30
ljain112
2068299766 fix: prevent precision errors in discount distribution with inclusive tax 2026-01-23 20:30:07 +05:30
Rohit Waghchaure
f5378b6573 fix: Bin reserved qty for production for extra material transfer 2026-01-23 19:35:47 +05:30
Diptanil Saha
c4e35f1284 Merge pull request #51976 from diptanilsaha/gh_51348 2026-01-23 16:30:27 +05:30
Mihir Kandoi
6a9c0e22de Merge pull request #51999 from aerele/support/fix-58134 2026-01-23 14:03:26 +05:30
rohitwaghchaure
809b29fe90 Merge pull request #52006 from rohitwaghchaure/fixed-negative-stock-for-purchase-return
fix: negative stock for purchase return
2026-01-23 11:33:25 +05:30
Rohit Waghchaure
d68a04ad16 fix: negative stock for purchae return 2026-01-23 01:19:52 +05:30
rohitwaghchaure
6954811c55 Merge pull request #52003 from rohitwaghchaure/fixed-hide-stock-entry-button
fix: do not show stock entry button if timer is running
2026-01-22 23:56:54 +05:30
rohitwaghchaure
8cdc21c264 Merge pull request #51989 from aerele/fix/autofill-warehouse
fix: autofill warehouse for packed items
2026-01-22 22:57:16 +05:30
Rohit Waghchaure
466668c6b8 fix: do not show stock entry button if timer is running 2026-01-22 22:56:34 +05:30
MochaMind
e7e22c809e fix: sync translations from crowdin (#51913)
* fix: Arabic translations

* fix: Arabic translations

* fix: Arabic translations

* fix: Arabic translations
2026-01-22 13:52:18 +00:00
Bharathidhasan06
2606ca6fa9 fix(stock): use purchase UOM in Supplier Quotation items 2026-01-22 18:07:36 +05:30
diptanilsaha
54acaa2aec fix(payment_reconciliation): retain journal entry accounts child table order 2026-01-22 14:06:56 +05:30
Sudharsanan11
3f8a0a4833 fix: autofill warehouse for packed items 2026-01-22 12:54:51 +05:30
SowmyaArunachalam
9e51701e2a fix(sales order): set project at item level from parent 2026-01-21 16:11:04 +05:30
ravibharathi656
57bd1facf5 fix: group item wise tax details by tax row 2026-01-20 12:31:30 +05:30
ravibharathi656
a378fee8e0 fix: include credit notes in project gross margin calculation 2026-01-20 08:47:58 +05:30
Abdeali Chharchhoda
63d71ff90a Merge branch 'develop' into fixing-emp-contacts 2026-01-19 17:55:55 +05:30
Mihir Kandoi
e2c3d0fa94 test: add test case 2026-01-16 14:52:49 +05:30
Mihir Kandoi
b8d4522ea1 chore: make feature opt in 2026-01-16 14:00:42 +05:30
Mihir Kandoi
22fd1a1cfd feat: document naming rule will now use posting date of the document 2026-01-16 14:00:42 +05:30
jacob-salvi
a1192e34d7 chore: new icons share-management 2026-01-12 15:14:59 +05:30
elshafei-developer
3e39d13172 fix(gross profit report): translate column Sales Invoice 2026-01-12 06:56:13 +00:00
Abdeali Chharchhoda
7c7ba0154a refactor: remove redundant onload function for bank mapping table 2026-01-12 00:23:02 +05:30
ravibharathi656
02e96039ac fix: correct exchange gain loss in ppr 2026-01-10 17:25:42 +05:30
Matt Howard
323636b396 fix(postgres): avoid UNSIGNED cast in customer autoname 2026-01-06 15:13:30 -05:00
Abdeali Chharchhoda
58cdb9503b refactor: method to get employee contact without permission check 2025-12-19 11:39:02 +05:30
Abdeali Chharchhoda
ec1eb6d222 refactor: use common method to get employee contacts 2025-12-18 18:48:09 +05:30
Abdeali Chharchhoda
7b89c12470 fix: get employee email with priority if preferred is not set 2025-12-18 18:41:31 +05:30
Abdeali Chharchhoda
b8e06b9636 refactor: add validation for missing employee parameter 2025-11-02 19:32:34 +05:30
Abdeali Chharchhoda
2ea6508fa5 refactor: fetch employee contact details in realtime 2025-11-01 20:18:52 +05:30
Abdeali Chharchhoda
a41297d841 feat: retrieve employee contact details 2025-11-01 19:18:23 +05:30
Abdeali Chharchhoda
4ad1474e32 feat: retrieve employee basic contact information 2025-11-01 17:55:09 +05:30
Abdeali Chharchhoda
87c59f471c chore: Removing unused import 2025-11-01 17:32:52 +05:30
567 changed files with 179859 additions and 131508 deletions

View File

@@ -60,7 +60,7 @@ body:
description: Share exact version number of Frappe and ERPNext you are using.
placeholder: |
Frappe version -
ERPNext Verion -
ERPNext version -
validations:
required: true

39
.github/helper/locale_review.js vendored Normal file
View File

@@ -0,0 +1,39 @@
module.exports = async ({ github, context, localeChanged }) => {
const pr = context.payload.pull_request.number
const { owner, repo } = context.repo
const { data: me } = await github.rest.users.getAuthenticated()
const botUser = me.login
const reviews = await github.rest.pulls.listReviews({
owner,
repo,
pull_number: pr
})
const botReview = reviews.data
.reverse()
.find(r => r.user.login === botUser && r.state === "CHANGES_REQUESTED")
if (localeChanged) {
if (!botReview) {
await github.rest.pulls.createReview({
owner,
repo,
pull_number: pr,
event: "REQUEST_CHANGES",
body: "Translations are managed through [Crowdin](https://crowdin.com/project/frappe/). Consider contributing through Crowdin."
})
}
} else {
if (botReview) {
await github.rest.pulls.dismissReview({
owner,
repo,
pull_number: pr,
review_id: botReview.id,
message: ""
})
}
}
}

View File

@@ -0,0 +1,44 @@
name: Prevent Locale Changes
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
check-locale:
permissions:
contents: read
runs-on: ubuntu-latest
if: github.actor != 'frappe-pr-bot'
# Exemption for the bot used for Crowdin Sync and POT File generation.
concurrency:
group: prevent-locale-changes-${{ github.event.pull_request.number }}
cancel-in-progress: true
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect locale changes
id: check
run: |
git fetch origin ${{ github.base_ref }}
if git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -q '^erpnext/locale/'; then
echo "locale_changed=true" >> $GITHUB_OUTPUT
else
echo "locale_changed=false" >> $GITHUB_OUTPUT
fi
- name: Manage locale review
uses: actions/github-script@v7
with:
github-token: ${{ secrets.RELEASE_TOKEN }}
script: |
const localeChanged = "${{ steps.check.outputs.locale_changed }}" === "true"
const handler = require('./.github/helper/locale_review.js')
await handler({ github, context, localeChanged })

View File

@@ -7,6 +7,7 @@ on:
paths:
- "**.js"
- "**.css"
- "**.svg"
- "**.md"
- "**.html"
- 'crowdin.yml'

View File

@@ -1,3 +1,4 @@
<div align="center">
<a href="https://frappe.io/erpnext">
<img src="./erpnext/public/images/v16/erpnext.svg" alt="ERPNext Logo" height="80px" width="80xp"/>

View File

@@ -1,7 +1,7 @@
# Security Policy
The ERPNext team and community take security issues seriously. To report a security issue, fill out the form at [https://erpnext.com/security/report](https://erpnext.com/security/report).
The ERPNext team and community take security issues seriously. To report a security issue, please go through the information mentioned [here](https://frappe.io/security).
You can help us make ERPNext and all it's users more secure by following the [Reporting guidelines](https://erpnext.com/security).
You can help us make ERPNext and all its users more secure by following the [Reporting guidelines](https://frappe.io/security).
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.

View File

@@ -0,0 +1,50 @@
{
"cards": [
{
"card": "Total Outgoing Bills"
},
{
"card": "Total Incoming Bills"
},
{
"card": "Total Incoming Payment"
},
{
"card": "Total Outgoing Payment"
}
],
"charts": [
{
"chart": "Incoming Bills (Purchase Invoice)",
"width": "Half"
},
{
"chart": "Outgoing Bills (Sales Invoice)",
"width": "Half"
},
{
"chart": "Accounts Receivable Ageing",
"width": "Half"
},
{
"chart": "Accounts Payable Ageing",
"width": "Half"
},
{
"chart": "Bank Balance",
"width": "Full"
}
],
"creation": "2026-01-26 21:25:12.793893",
"dashboard_name": "Payments",
"docstatus": 0,
"doctype": "Dashboard",
"idx": 0,
"is_default": 0,
"is_standard": 1,
"modified": "2026-01-26 21:25:12.793893",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payments",
"owner": "Administrator"
}

View File

@@ -52,7 +52,7 @@ class ERPNextAddress(Address):
@frappe.whitelist()
def get_shipping_address(company, address=None):
def get_shipping_address(company: str, address: str | None = None):
filters = [
["Dynamic Link", "link_doctype", "=", "Company"],
["Dynamic Link", "link_name", "=", company],

View File

@@ -13,15 +13,15 @@ from frappe.utils.nestedset import get_descendants_of
@frappe.whitelist()
@cache_source
def get(
chart_name=None,
chart=None,
no_cache=None,
filters=None,
from_date=None,
to_date=None,
timespan=None,
time_interval=None,
heatmap_year=None,
chart_name: str | None = None,
chart: str | dict | None = None,
no_cache: bool | None = None,
filters: str | dict | None = None,
from_date: str | None = None,
to_date: str | None = None,
timespan: str | None = None,
time_interval: str | None = None,
heatmap_year: str | None = None,
):
if chart_name:
chart = frappe.get_doc("Dashboard Chart", chart_name)

View File

@@ -471,7 +471,7 @@ class Account(NestedSet):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
def get_parent_account(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
return frappe.db.sql(
"""select name from tabAccount
where is_group = 1 and docstatus != 2 and company = {}
@@ -515,7 +515,9 @@ def get_account_autoname(account_number, account_name, company):
@frappe.whitelist()
def update_account_number(name, account_name, account_number=None, from_descendant=False):
def update_account_number(
name: str, account_name: str, account_number: str | None = None, from_descendant: bool = False
):
_ensure_idle_system()
account = frappe.get_cached_doc("Account", name)
if not account:
@@ -577,7 +579,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
@frappe.whitelist()
def merge_account(old, new):
def merge_account(old: str, new: str):
_ensure_idle_system()
# Validate properties before merging
new_account = frappe.get_cached_doc("Account", new)
@@ -614,7 +616,7 @@ def merge_account(old, new):
@frappe.whitelist()
def get_root_company(company):
def get_root_company(company: str):
# return the topmost company in the hierarchy
ancestors = get_ancestors_of("Company", company, "lft asc")
return [ancestors[0]] if ancestors else []

View File

@@ -99,7 +99,7 @@ def identify_is_group(child):
@frappe.whitelist()
def get_chart(chart_template, existing_company=None):
def get_chart(chart_template: str | None, existing_company: str | None = None):
chart = {}
if existing_company:
return get_account_tree_from_existing_company(existing_company)
@@ -132,7 +132,7 @@ def get_chart(chart_template, existing_company=None):
@frappe.whitelist()
def get_charts_for_country(country, with_standard=False):
def get_charts_for_country(country: str, with_standard: bool = False):
charts = []
def _get_chart_name(content):
@@ -225,7 +225,7 @@ def build_account_tree(tree, parent, all_accounts):
@frappe.whitelist()
def validate_bank_account(coa, bank_account):
def validate_bank_account(coa: str, bank_account: str):
accounts = []
chart = get_chart(coa)
@@ -244,7 +244,9 @@ def validate_bank_account(coa, bank_account):
@frappe.whitelist()
def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=False):
def build_tree_from_json(
chart_template: str, chart_data: dict | None = None, from_coa_importer: bool = False
):
"""get chart template from its folder and parse the json to be rendered as tree"""
chart = chart_data or get_chart(chart_template)

View File

@@ -33,6 +33,17 @@
},
"account_number": "1151.000"
},
"Pajak Dibayar di Muka": {
"PPN Masukan": {
"account_number": "1152.001",
"account_type": "Tax"
},
"PPh 23 Dibayar di Muka": {
"account_number": "1152.002",
"account_type": "Tax"
},
"account_number": "1152.000"
},
"account_number": "1150.000"
},
"Kas": {
@@ -97,17 +108,6 @@
},
"account_number": "1130.000"
},
"Pajak Dibayar di Muka": {
"PPN Masukan": {
"account_number": "1151.001",
"account_type": "Tax"
},
"PPh 23 Dibayar di Muka": {
"account_number": "1152.001",
"account_type": "Tax"
},
"account_number": "1150.000"
},
"account_number": "1100.000"
},

View File

@@ -103,10 +103,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:
@@ -210,7 +206,7 @@ def delete_accounting_dimension(doc):
@frappe.whitelist()
def disable_dimension(doc):
def disable_dimension(doc: str):
if frappe.in_test:
toggle_disabling(doc=doc)
else:
@@ -241,34 +237,26 @@ def get_doctypes_with_dimensions():
return frappe.get_hooks("accounting_dimension_doctypes")
def get_accounting_dimensions(as_list=True, filters=None):
if not filters:
filters = {"disabled": 0}
if frappe.flags.accounting_dimensions is None:
frappe.flags.accounting_dimensions = frappe.get_all(
"Accounting Dimension",
fields=["label", "fieldname", "disabled", "document_type"],
filters=filters,
)
def get_accounting_dimensions(as_list=True):
accounting_dimensions = frappe.get_all(
"Accounting Dimension",
fields=["label", "fieldname", "disabled", "document_type"],
filters={"disabled": 0},
)
if as_list:
return [d.fieldname for d in frappe.flags.accounting_dimensions]
return [d.fieldname for d in accounting_dimensions]
else:
return frappe.flags.accounting_dimensions
return accounting_dimensions
def get_checks_for_pl_and_bs_accounts():
if frappe.flags.accounting_dimensions_details is None:
# nosemgrep
frappe.flags.accounting_dimensions_details = frappe.db.sql(
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
return frappe.db.sql(
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
WHERE p.name = c.parent AND p.disabled = 0""",
as_dict=1,
)
return frappe.flags.accounting_dimensions_details
as_dict=1,
)
def get_dimension_with_children(doctype, dimensions):
@@ -286,7 +274,7 @@ def get_dimension_with_children(doctype, dimensions):
@frappe.whitelist()
def get_dimensions(with_cost_center_and_project=False):
def get_dimensions(with_cost_center_and_project: str | bool = False):
c = frappe.qb.DocType("Accounting Dimension Detail")
p = frappe.qb.DocType("Accounting Dimension")
dimension_filters = (

View File

@@ -69,37 +69,34 @@ class AccountingDimensionFilter(Document):
def get_dimension_filter_map():
if not frappe.flags.get("dimension_filter_map"):
filters = frappe.db.sql(
"""
SELECT
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
p.allow_or_restrict, p.fieldname, a.is_mandatory
FROM
`tabApplicable On Account` a,
`tabAccounting Dimension Filter` p
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
WHERE
p.name = a.parent
AND p.disabled = 0
""",
as_dict=1,
filters = frappe.db.sql(
"""
SELECT
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
p.allow_or_restrict, p.fieldname, a.is_mandatory
FROM
`tabApplicable On Account` a,
`tabAccounting Dimension Filter` p
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
WHERE
p.name = a.parent
AND p.disabled = 0
""",
as_dict=1,
)
dimension_filter_map = {}
for f in filters:
build_map(
dimension_filter_map,
f.fieldname,
f.applicable_on_account,
f.dimension_value,
f.allow_or_restrict,
f.is_mandatory,
)
dimension_filter_map = {}
for f in filters:
build_map(
dimension_filter_map,
f.fieldname,
f.applicable_on_account,
f.dimension_value,
f.allow_or_restrict,
f.is_mandatory,
)
frappe.flags.dimension_filter_map = dimension_filter_map
return frappe.flags.dimension_filter_map
return dimension_filter_map
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):

View File

@@ -97,7 +97,7 @@ def validate_accounting_period_on_doc_save(doc, method=None):
if doc.doctype == "Bank Clearance":
return
elif doc.doctype == "Asset":
if doc.is_existing_asset:
if doc.asset_type == "Existing Asset":
return
else:
date = doc.available_for_use_date

View File

@@ -20,6 +20,10 @@
"enable_common_party_accounting",
"allow_multi_currency_invoices_against_single_party_account",
"confirm_before_resetting_posting_date",
"analytics_section",
"enable_accounting_dimensions",
"column_break_vtnr",
"enable_discounts_and_margin",
"journals_section",
"merge_similar_account_heads",
"deferred_accounting_settings_section",
@@ -51,12 +55,16 @@
"allow_pegged_currencies_exchange_rates",
"column_break_yuug",
"stale_days",
"payments_tab",
"section_break_jpd0",
"auto_reconcile_payments",
"auto_reconciliation_job_trigger",
"reconciliation_queue_size",
"column_break_resa",
"exchange_gain_loss_posting_date",
"payment_options_section",
"enable_loyalty_point_program",
"column_break_ctam",
"invoicing_settings_tab",
"accounts_transactions_settings_section",
"over_billing_allowance",
@@ -197,7 +205,7 @@
"description": "Payment Terms from orders will be fetched into the invoices as is",
"fieldname": "automatically_fetch_payment_terms",
"fieldtype": "Check",
"label": "Automatically Fetch Payment Terms from Order"
"label": "Automatically Fetch Payment Terms from Order/Quotation"
},
{
"description": "The percentage you are allowed to bill more against the amount ordered. For example, if the order value is $100 for an item and tolerance is set as 10%, then you are allowed to bill up to $110 ",
@@ -281,7 +289,7 @@
},
{
"default": "0",
"description": "Learn about <a href=\"https://docs.frappe.io/erpnext/user/manual/en/common_party_accounting\">Common Party</a>",
"description": "Learn about <a href=\"https://docs.frappe.io/erpnext/user/manual/en/common_party_accounting\" rel=\"noopener noreferrer\">Common Party</a>",
"fieldname": "enable_common_party_accounting",
"fieldtype": "Check",
"label": "Enable Common Party Accounting"
@@ -637,16 +645,59 @@
"fieldname": "budget_section",
"fieldtype": "Section Break",
"label": "Budget"
},
{
"fieldname": "analytics_section",
"fieldtype": "Section Break",
"label": "Analytical Accounting"
},
{
"fieldname": "column_break_vtnr",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "Apply discounts and margins on products",
"fieldname": "enable_discounts_and_margin",
"fieldtype": "Check",
"label": "Enable Discounts and Margin"
},
{
"fieldname": "payments_tab",
"fieldtype": "Tab Break",
"label": "Payments"
},
{
"fieldname": "payment_options_section",
"fieldtype": "Section Break",
"label": "Payment Options"
},
{
"default": "0",
"fieldname": "enable_loyalty_point_program",
"fieldtype": "Check",
"label": "Enable Loyalty Point Program"
},
{
"fieldname": "column_break_ctam",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "Enable cost center, projects and other custom accounting dimensions",
"fieldname": "enable_accounting_dimensions",
"fieldtype": "Check",
"label": "Enable Accounting Dimensions"
}
],
"grid_page_length": 50,
"hide_toolbar": 1,
"hide_toolbar": 0,
"icon": "icon-cog",
"idx": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2026-01-11 18:30:45.968531",
"modified": "2026-02-27 01:04:09.415288",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -12,6 +12,28 @@ from frappe.utils import cint
from erpnext.accounts.utils import sync_auto_reconcile_config
SELLING_DOCTYPES = [
"Sales Invoice",
"Sales Order",
"Delivery Note",
"Quotation",
"Sales Invoice Item",
"Sales Order Item",
"Delivery Note Item",
"Quotation Item",
"POS Invoice",
"POS Invoice Item",
]
BUYING_DOCTYPES = [
"Purchase Invoice",
"Purchase Order",
"Purchase Receipt",
"Purchase Invoice Item",
"Purchase Order Item",
"Purchase Receipt Item",
]
class AccountsSettings(Document):
# begin: auto-generated types
@@ -43,9 +65,12 @@ class AccountsSettings(Document):
default_ageing_range: DF.Data | None
delete_linked_ledger_entries: DF.Check
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]
enable_accounting_dimensions: DF.Check
enable_common_party_accounting: DF.Check
enable_discounts_and_margin: DF.Check
enable_fuzzy_matching: DF.Check
enable_immutable_ledger: DF.Check
enable_loyalty_point_program: DF.Check
enable_party_matching: DF.Check
exchange_gain_loss_posting_date: DF.Literal["Invoice", "Payment", "Reconciliation Date"]
fetch_valuation_rate_for_internal_transaction: DF.Check
@@ -98,6 +123,18 @@ class AccountsSettings(Document):
if old_doc.show_payment_schedule_in_print != self.show_payment_schedule_in_print:
self.enable_payment_schedule_in_print()
if old_doc.enable_accounting_dimensions != self.enable_accounting_dimensions:
toggle_accounting_dimension_sections(not self.enable_accounting_dimensions)
clear_cache = True
if old_doc.enable_discounts_and_margin != self.enable_discounts_and_margin:
toggle_sales_discount_section(not self.enable_discounts_and_margin)
clear_cache = True
if old_doc.enable_loyalty_point_program != self.enable_loyalty_point_program:
toggle_loyalty_point_program_section(not self.enable_loyalty_point_program)
clear_cache = True
if clear_cache:
frappe.clear_cache()
@@ -154,3 +191,36 @@ class AccountsSettings(Document):
frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.init_procedure_name}")
frappe.db.sql(f"drop procedure if exists {InitSQLProceduresForAR.allocate_procedure_name}")
def toggle_accounting_dimension_sections(hide):
accounting_dimension_doctypes = frappe.get_hooks("accounting_dimension_doctypes")
for doctype in accounting_dimension_doctypes:
create_property_setter_for_hiding_field(doctype, "accounting_dimensions_section", hide)
def toggle_sales_discount_section(hide):
for doctype in SELLING_DOCTYPES + BUYING_DOCTYPES:
meta = frappe.get_meta(doctype)
if meta.has_field("additional_discount_section"):
create_property_setter_for_hiding_field(doctype, "additional_discount_section", hide)
if meta.has_field("discount_and_margin"):
create_property_setter_for_hiding_field(doctype, "discount_and_margin", hide)
def toggle_loyalty_point_program_section(hide):
for doctype in SELLING_DOCTYPES:
meta = frappe.get_meta(doctype)
if meta.has_field("loyalty_points_redemption"):
create_property_setter_for_hiding_field(doctype, "loyalty_points_redemption", hide)
def create_property_setter_for_hiding_field(doctype, field_name, hide):
make_property_setter(
doctype,
field_name,
"hidden",
hide,
"Check",
validate_fields_for_doctype=False,
)

View File

@@ -50,6 +50,7 @@
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount",
"options": "currency",
"read_only": 1
},
{

View File

@@ -3,9 +3,6 @@
frappe.provide("erpnext.integrations");
frappe.ui.form.on("Bank", {
onload: function (frm) {
add_fields_to_mapping_table(frm);
},
refresh: function (frm) {
add_fields_to_mapping_table(frm);
frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal);
@@ -37,11 +34,11 @@ let add_fields_to_mapping_table = function (frm) {
});
});
frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property(
"bank_transaction_field",
"options",
options
);
const grid = frm.fields_dict.bank_transaction_mapping?.grid;
if (grid) {
grid.update_docfield_property("bank_transaction_field", "options", options);
}
};
erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
@@ -116,7 +113,7 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
"There was an issue connecting to Plaid's authentication server. Check browser console for more information"
)
);
console.log(error);
console.error(error);
}
plaid_success(token, response) {

View File

@@ -115,7 +115,7 @@ def get_default_company_bank_account(company, party_type, party):
@frappe.whitelist()
def get_bank_account_details(bank_account):
def get_bank_account_details(bank_account: str):
return frappe.get_cached_value(
"Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1
)

View File

@@ -1,8 +1,8 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
from datetime import date
import frappe
from frappe import _
@@ -47,7 +47,9 @@ class BankReconciliationTool(Document):
@frappe.whitelist()
def get_bank_transactions(bank_account, from_date=None, to_date=None):
def get_bank_transactions(
bank_account: str, from_date: str | date | None = None, to_date: str | date | None = None
):
# returns bank transactions for a bank account
filters = []
filters.append(["bank_account", "=", bank_account])
@@ -80,7 +82,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
@frappe.whitelist()
def get_account_balance(bank_account, till_date, company):
def get_account_balance(bank_account: str, till_date: str | date, company: str):
# returns account balance till the specified date
account = frappe.db.get_value("Bank Account", bank_account, "account")
filters = frappe._dict(
@@ -106,7 +108,9 @@ def get_account_balance(bank_account, till_date, company):
@frappe.whitelist()
def update_bank_transaction(bank_transaction_name, reference_number, party_type=None, party=None):
def update_bank_transaction(
bank_transaction_name: str, reference_number: str, party_type: str | None = None, party: str | None = None
):
# updates bank transaction based on the new parameters provided by the user from Vouchers
bank_transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
bank_transaction.reference_number = reference_number
@@ -135,16 +139,16 @@ def update_bank_transaction(bank_transaction_name, reference_number, party_type=
@frappe.whitelist()
def create_journal_entry_bts(
bank_transaction_name,
reference_number=None,
reference_date=None,
posting_date=None,
entry_type=None,
second_account=None,
mode_of_payment=None,
party_type=None,
party=None,
allow_edit=None,
bank_transaction_name: str,
reference_number: str | None = None,
reference_date: str | None = None,
posting_date: str | date | None = None,
entry_type: str | None = None,
second_account: str | None = None,
mode_of_payment: str | None = None,
party_type: str | None = None,
party: str | None = None,
allow_edit: bool | None = None,
):
# Create a new journal entry based on the bank transaction
bank_transaction = frappe.db.get_values(
@@ -294,17 +298,17 @@ def create_journal_entry_bts(
@frappe.whitelist()
def create_payment_entry_bts(
bank_transaction_name,
reference_number=None,
reference_date=None,
party_type=None,
party=None,
posting_date=None,
mode_of_payment=None,
project=None,
cost_center=None,
allow_edit=None,
company_bank_account=None,
bank_transaction_name: str,
reference_number: str | None = None,
reference_date: str | None = None,
party_type: str | None = None,
party: str | None = None,
posting_date: str | None = None,
mode_of_payment: str | None = None,
project: str | None = None,
cost_center: str | None = None,
allow_edit: bool | None = None,
company_bank_account: str | None = None,
):
# Create a new payment entry based on the bank transaction
bank_transaction = frappe.db.get_values(
@@ -371,12 +375,12 @@ def create_payment_entry_bts(
@frappe.whitelist()
def auto_reconcile_vouchers(
bank_account,
from_date=None,
to_date=None,
filter_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
bank_account: str,
from_date: str | date | None = None,
to_date: str | date | None = None,
filter_by_reference_date: bool | None = None,
from_reference_date: bool | None = None,
to_reference_date: str | None = None,
):
bank_transactions = get_bank_transactions(bank_account)
@@ -471,7 +475,7 @@ def get_auto_reconcile_message(partially_reconciled, reconciled):
@frappe.whitelist()
def reconcile_vouchers(bank_transaction_name, vouchers):
def reconcile_vouchers(bank_transaction_name: str, vouchers: str):
# updated clear date of all the vouchers based on the bank transaction
vouchers = json.loads(vouchers)
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
@@ -487,13 +491,13 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
@frappe.whitelist()
def get_linked_payments(
bank_transaction_name,
document_types=None,
from_date=None,
to_date=None,
filter_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
bank_transaction_name: str,
document_types: str | list[str] | None = None,
from_date: str | date | None = None,
to_date: str | date | None = None,
filter_by_reference_date: bool | None = None,
from_reference_date: bool | None = None,
to_reference_date: str | None = None,
):
# get all matching payments for a bank transaction
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)

View File

@@ -143,7 +143,7 @@ def preprocess_mt940_content(content: str) -> str:
@frappe.whitelist()
def convert_mt940_to_csv(data_import, mt940_file_path):
def convert_mt940_to_csv(data_import: str, mt940_file_path: str):
doc = frappe.get_doc("Bank Statement Import", data_import)
_file_doc, content = get_file(mt940_file_path)
@@ -208,26 +208,28 @@ def convert_mt940_to_csv(data_import, mt940_file_path):
@frappe.whitelist()
def get_preview_from_template(data_import, import_file=None, google_sheets_url=None):
def get_preview_from_template(
data_import: str, import_file: str | None = None, google_sheets_url: str | None = None
):
return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template(
import_file, google_sheets_url
)
@frappe.whitelist()
def form_start_import(data_import):
def form_start_import(data_import: str):
job_id = frappe.get_doc("Bank Statement Import", data_import).start_import()
return job_id is not None
@frappe.whitelist()
def download_errored_template(data_import_name):
def download_errored_template(data_import_name: str):
data_import = frappe.get_doc("Bank Statement Import", data_import_name)
data_import.export_errored_rows()
@frappe.whitelist()
def download_import_log(data_import_name):
def download_import_log(data_import_name: str):
return frappe.get_doc("Bank Statement Import", data_import_name).download_import_log()
@@ -363,7 +365,7 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
@frappe.whitelist()
def get_import_status(docname):
def get_import_status(docname: str):
import_status = {}
data_import = frappe.get_doc("Bank Statement Import", docname)

View File

@@ -139,6 +139,8 @@ class BankTransaction(Document):
self.set_status()
def on_cancel(self):
self.ignore_linked_doctypes = ["GL Entry"]
for payment_entry in self.payment_entries:
self.delink_payment_entry(payment_entry)
@@ -373,11 +375,12 @@ def get_clearance_details(transaction, payment_entry, bt_allocations, gl_entries
("unallocated_amount", "bank_account"),
as_dict=True,
)
bt_bank_account = frappe.db.get_value("Bank Account", bt.bank_account, "account")
if bt.bank_account != gl_bank_account:
if bt_bank_account != gl_bank_account:
frappe.throw(
_("Bank Account {} in Bank Transaction {} is not matching with Bank Account {}").format(
bt.bank_account, payment_entry.payment_entry, gl_bank_account
bt_bank_account, payment_entry.payment_entry, gl_bank_account
)
)

View File

@@ -35,7 +35,7 @@ def upload_bank_statement():
@frappe.whitelist()
def create_bank_entries(columns, data, bank_account):
def create_bank_entries(columns: str, data: str, bank_account: str):
header_map = get_header_mapping(columns, bank_account)
success = 0

View File

@@ -845,7 +845,7 @@ def get_fiscal_year_date_range(from_fiscal_year, to_fiscal_year):
@frappe.whitelist()
def revise_budget(budget_name):
def revise_budget(budget_name: str):
old_budget = frappe.get_doc("Budget", budget_name)
if old_budget.docstatus == 1:

View File

@@ -57,7 +57,7 @@ def validate_columns(data):
@frappe.whitelist()
def validate_company(company):
def validate_company(company: str):
parent_company, allow_account_creation_against_child_company = frappe.get_cached_value(
"Company", company, ["parent_company", "allow_account_creation_against_child_company"]
)
@@ -74,7 +74,7 @@ def validate_company(company):
@frappe.whitelist()
def import_coa(file_name, company):
def import_coa(file_name: str, company: str):
# delete existing data for accounts
unset_existing_data(company)
@@ -159,7 +159,9 @@ def generate_data_from_excel(file_doc, extension, as_dict=False):
@frappe.whitelist()
def get_coa(doctype, parent, is_root=False, file_name=None, for_validate=0):
def get_coa(
doctype: str, parent: str, is_root: bool = False, file_name: str | None = None, for_validate: int = 0
):
"""called by tree view (to fetch node's children)"""
file_doc, extension = get_file(file_name)
@@ -307,7 +309,7 @@ def build_response_as_excel(writer):
@frappe.whitelist()
def download_template(file_type, template_type, company):
def download_template(file_type: str, template_type: str, company: str):
writer = get_template(template_type, company)
if file_type == "CSV":
@@ -361,7 +363,7 @@ def get_sample_template(writer, company):
@frappe.whitelist()
def validate_accounts(file_doc, extension):
def validate_accounts(file_doc: Document, extension: str):
if extension == "csv":
accounts = generate_data_from_csv(file_doc, as_dict=True)
else:

View File

@@ -47,7 +47,7 @@ class ChequePrintTemplate(Document):
@frappe.whitelist()
def create_or_update_cheque_print_format(template_name):
def create_or_update_cheque_print_format(template_name: str):
if not frappe.db.exists("Print Format", template_name):
cheque_print = frappe.new_doc("Print Format")
cheque_print.update(

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from datetime import date
import frappe
from frappe import _, qb
@@ -614,7 +615,12 @@ def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
@frappe.whitelist()
def get_account_details(
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float | None = None
company: str,
posting_date: str | date,
account: str,
party_type: str | None = None,
party: str | None = None,
rounding_loss_allowance: float = 0.0,
):
if not (company and posting_date):
frappe.throw(_("Company and Posting Date is mandatory"))

View File

@@ -15,7 +15,7 @@ from frappe.database.operator_map import OPERATOR_MAP
from frappe.query_builder import Case
from frappe.query_builder.functions import Sum
from frappe.utils import cstr, date_diff, flt, getdate
from pypika.terms import LiteralValue
from pypika.terms import Bracket, LiteralValue
from erpnext import get_company_currency
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -541,7 +541,7 @@ class FinancialQueryBuilder:
.where(acb_table.period_closing_voucher == closing_voucher)
)
query = self._apply_standard_filters(query, acb_table)
query = self._apply_standard_filters(query, acb_table, "Account Closing Balance")
results = self._execute_with_permissions(query, "Account Closing Balance")
for row in results:
@@ -636,12 +636,15 @@ class FinancialQueryBuilder:
return self._execute_with_permissions(query, "GL Entry")
def _calculate_running_balances(self, balances_data: dict, gl_data: list[dict]) -> dict:
for row in gl_data:
account = row["account"]
gl_dict = {row["account"]: row for row in gl_data}
accounts = set(balances_data.keys()) | set(gl_dict.keys())
for account in accounts:
if account not in balances_data:
balances_data[account] = AccountData(account=account, **self._get_account_meta(account))
account_data: AccountData = balances_data[account]
gl_movement = gl_dict.get(account, {})
if account_data.has_periods():
first_period = account_data.get_period(self.periods[0]["key"])
@@ -651,20 +654,13 @@ class FinancialQueryBuilder:
for period in self.periods:
period_key = period["key"]
movement = row.get(period_key, 0.0)
movement = gl_movement.get(period_key, 0.0)
closing_balance = current_balance + movement
account_data.add_period(PeriodValue(period_key, current_balance, closing_balance, movement))
current_balance = closing_balance
# Accounts with no movements
for account_data in balances_data.values():
for period in self.periods:
period_key = period["key"]
if period_key not in account_data.period_values:
account_data.add_period(PeriodValue(period_key, 0.0, 0.0, 0.0))
def _handle_balance_accumulation(self, balances_data):
for account_data in balances_data.values():
account_data: AccountData
@@ -683,12 +679,12 @@ class FinancialQueryBuilder:
else:
account_data.unaccumulate_values()
def _apply_standard_filters(self, query, table):
def _apply_standard_filters(self, query, table, doctype: str = "GL Entry"):
if self.filters.get("ignore_closing_entries"):
if hasattr(table, "is_period_closing_voucher_entry"):
query = query.where(table.is_period_closing_voucher_entry == 0)
else:
if doctype == "GL Entry":
query = query.where(table.voucher_type != "Period Closing Voucher")
else:
query = query.where(table.is_period_closing_voucher_entry == 0)
if self.filters.get("project"):
projects = self.filters.get("project")
@@ -736,7 +732,7 @@ class FinancialQueryBuilder:
user_conditions = build_match_conditions(doctype)
if user_conditions:
query = query.where(LiteralValue(user_conditions))
query = query.where(Bracket(LiteralValue(user_conditions)))
return query.run(as_dict=True)

View File

@@ -7,12 +7,14 @@ from frappe.utils import flt
from erpnext.accounts.doctype.financial_report_template.financial_report_engine import (
DependencyResolver,
FilterExpressionParser,
FinancialQueryBuilder,
FormulaCalculator,
)
from erpnext.accounts.doctype.financial_report_template.test_financial_report_template import (
FinancialReportTemplateTestCase,
)
from erpnext.accounts.utils import get_currency_precision
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.utils import get_currency_precision, get_fiscal_year
# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
@@ -1668,3 +1670,360 @@ class TestFilterExpressionParser(FinancialReportTemplateTestCase):
mock_row_invalid = self._create_mock_report_row(invalid_formula)
condition = parser.build_condition(mock_row_invalid, account_table)
self.assertIsNone(condition)
class TestFinancialQueryBuilder(FinancialReportTemplateTestCase):
def test_fetch_balances_with_journal_entries(self):
company = "_Test Company"
cash_account = "_Test Cash - _TC"
bank_account = "_Test Bank - _TC"
# Create journal entries in different periods
# October: Transfer 1000 from Bank to Cash
jv_oct = make_journal_entry(
account1=cash_account,
account2=bank_account,
amount=1000,
posting_date="2024-10-15",
company=company,
submit=True,
)
# November: Transfer 500 from Bank to Cash
jv_nov = make_journal_entry(
account1=cash_account,
account2=bank_account,
amount=500,
posting_date="2024-11-20",
company=company,
submit=True,
)
# December: No transactions (test zero movement period)
try:
# Set up filters and periods for Q4 2024
filters = {
"company": company,
"from_fiscal_year": "2024",
"to_fiscal_year": "2024",
"period_start_date": "2024-10-01",
"period_end_date": "2024-12-31",
"filter_based_on": "Date Range",
"periodicity": "Monthly",
}
periods = [
{"key": "2024_oct", "from_date": "2024-10-01", "to_date": "2024-10-31"},
{"key": "2024_nov", "from_date": "2024-11-01", "to_date": "2024-11-30"},
{"key": "2024_dec", "from_date": "2024-12-01", "to_date": "2024-12-31"},
]
query_builder = FinancialQueryBuilder(filters, periods)
# Create account objects as expected by fetch_account_balances
accounts = [
frappe._dict({"name": cash_account, "account_name": "Cash", "account_number": "1001"}),
frappe._dict({"name": bank_account, "account_name": "Bank", "account_number": "1002"}),
]
# Fetch balances using the full workflow
balances_data = query_builder.fetch_account_balances(accounts)
# Verify Cash account balances
cash_data = balances_data.get(cash_account)
self.assertIsNotNone(cash_data, "Cash account should exist in results")
# October: movement = +1000 (debit)
oct_cash = cash_data.get_period("2024_oct")
self.assertIsNotNone(oct_cash, "October period should exist for cash")
self.assertEqual(oct_cash.movement, 1000.0, "October cash movement should be 1000")
# November: movement = +500
nov_cash = cash_data.get_period("2024_nov")
self.assertIsNotNone(nov_cash, "November period should exist for cash")
self.assertEqual(nov_cash.movement, 500.0, "November cash movement should be 500")
self.assertEqual(
nov_cash.opening, oct_cash.closing, "November opening should equal October closing"
)
# December: movement = 0 (no transactions)
dec_cash = cash_data.get_period("2024_dec")
self.assertIsNotNone(dec_cash, "December period should exist for cash")
self.assertEqual(dec_cash.movement, 0.0, "December cash movement should be 0")
self.assertEqual(
dec_cash.closing,
nov_cash.closing,
"December closing should equal November closing when no movement",
)
# Verify Bank account balances (opposite direction)
bank_data = balances_data.get(bank_account)
self.assertIsNotNone(bank_data, "Bank account should exist in results")
oct_bank = bank_data.get_period("2024_oct")
self.assertEqual(oct_bank.movement, -1000.0, "October bank movement should be -1000")
nov_bank = bank_data.get_period("2024_nov")
self.assertEqual(nov_bank.movement, -500.0, "November bank movement should be -500")
finally:
# Clean up: cancel journal entries
jv_nov.cancel()
jv_oct.cancel()
def test_opening_balance_from_previous_period_closing(self):
company = "_Test Company"
cash_account = "_Test Cash - _TC"
sales_account = "Sales - _TC"
posting_date_2023 = "2023-06-15"
# Create journal entry in prior period (2023)
# Cash Dr 5000, Sales Cr 5000
jv_2023 = make_journal_entry(
account1=cash_account,
account2=sales_account,
amount=5000,
posting_date=posting_date_2023,
company=company,
submit=True,
)
pcv = None
jv_2024 = None
original_pcv_setting = frappe.db.get_single_value(
"Accounts Settings", "use_legacy_controller_for_pcv"
)
try:
# Create Period Closing Voucher for 2023
# This will create Account Closing Balance entries
closing_account = frappe.db.get_value(
"Account",
{
"company": company,
"root_type": "Liability",
"is_group": 0,
"account_type": ["not in", ["Payable", "Receivable"]],
},
"name",
)
fy_2023 = get_fiscal_year(posting_date_2023, company=company)
frappe.db.set_single_value("Accounts Settings", "use_legacy_controller_for_pcv", 1)
pcv = frappe.get_doc(
{
"doctype": "Period Closing Voucher",
"transaction_date": "2023-12-31",
"period_start_date": fy_2023[1],
"period_end_date": fy_2023[2],
"company": company,
"fiscal_year": fy_2023[0],
"cost_center": "_Test Cost Center - _TC",
"closing_account_head": closing_account,
"remarks": "Test Period Closing",
}
)
pcv.insert()
pcv.submit()
pcv.reload()
# Now create a small transaction in 2024 to ensure the account appears
jv_2024 = make_journal_entry(
account1=cash_account,
account2=sales_account,
amount=100,
posting_date="2024-01-15",
company=company,
submit=True,
)
# Set up filters for Q1 2024 (after the period closing)
filters = {
"company": company,
"from_fiscal_year": "2024",
"to_fiscal_year": "2024",
"period_start_date": "2024-01-01",
"period_end_date": "2024-03-31",
"filter_based_on": "Date Range",
"periodicity": "Monthly",
"ignore_closing_entries": True, # Don't include PCV entries in movements
}
periods = [
{"key": "2024_jan", "from_date": "2024-01-01", "to_date": "2024-01-31"},
{"key": "2024_feb", "from_date": "2024-02-01", "to_date": "2024-02-29"},
{"key": "2024_mar", "from_date": "2024-03-01", "to_date": "2024-03-31"},
]
query_builder = FinancialQueryBuilder(filters, periods)
accounts = [
frappe._dict({"name": cash_account, "account_name": "Cash", "account_number": "1001"}),
]
balances_data = query_builder.fetch_account_balances(accounts)
# Verify Cash account has opening balance from 2023 transactions
cash_data = balances_data.get(cash_account)
self.assertIsNotNone(cash_data, "Cash account should exist in results")
jan_cash = cash_data.get_period("2024_jan")
self.assertIsNotNone(jan_cash, "January period should exist")
# Opening balance should be from prior period
# Cash had 5000 debit in 2023, so opening in 2024 should be >= 5000
# (may be higher if there were other test transactions)
self.assertEqual(
jan_cash.opening,
5000.0,
"January opening should equal to balance from 2023 (5000)",
)
# Verify running balance logic
# Movement in January is 100 (from jv_2024)
self.assertEqual(jan_cash.movement, 100.0, "January movement should be 100")
self.assertEqual(
jan_cash.closing, jan_cash.opening + jan_cash.movement, "Closing = Opening + Movement"
)
# February and March should have no movement but carry the balance
feb_cash = cash_data.get_period("2024_feb")
self.assertEqual(feb_cash.opening, jan_cash.closing, "Feb opening = Jan closing")
self.assertEqual(feb_cash.movement, 0.0, "February should have no movement")
self.assertEqual(feb_cash.closing, feb_cash.opening, "Feb closing = opening when no movement")
mar_cash = cash_data.get_period("2024_mar")
self.assertEqual(mar_cash.opening, feb_cash.closing, "Mar opening = Feb closing")
self.assertEqual(mar_cash.movement, 0.0, "March should have no movement")
self.assertEqual(mar_cash.closing, mar_cash.opening, "Mar closing = opening when no movement")
# Set up filters for Q2 2024
filters_q2 = {
"company": company,
"from_fiscal_year": "2024",
"to_fiscal_year": "2024",
"period_start_date": "2024-04-01",
"period_end_date": "2024-06-30",
"filter_based_on": "Date Range",
"periodicity": "Monthly",
"ignore_closing_entries": True,
}
periods_q2 = [
{"key": "2024_apr", "from_date": "2024-04-01", "to_date": "2024-04-30"},
{"key": "2024_may", "from_date": "2024-05-01", "to_date": "2024-05-31"},
{"key": "2024_jun", "from_date": "2024-06-01", "to_date": "2024-06-30"},
]
query_builder_q2 = FinancialQueryBuilder(filters_q2, periods_q2)
balances_data_q2 = query_builder_q2.fetch_account_balances(accounts)
# Verify Cash account in Q2
cash_data_q2 = balances_data_q2.get(cash_account)
self.assertIsNotNone(cash_data_q2, "Cash account should exist in Q2 results")
apr_cash = cash_data_q2.get_period("2024_apr")
self.assertIsNotNone(apr_cash, "April period should exist")
# Opening balance in April should equal closing in March
self.assertEqual(
apr_cash.opening,
mar_cash.closing,
"April opening should equal March closing balance",
)
self.assertEqual(apr_cash.closing, apr_cash.opening, "April closing = opening when no movement")
finally:
# Clean up
frappe.db.set_single_value(
"Accounts Settings", "use_legacy_controller_for_pcv", original_pcv_setting or 0
)
if jv_2024:
jv_2024.cancel()
if pcv:
pcv.reload()
if pcv.docstatus == 1:
pcv.cancel()
jv_2023.cancel()
def test_account_with_gl_entries_but_no_prior_closing_balance(self):
company = "_Test Company"
cash_account = "_Test Cash - _TC"
bank_account = "_Test Bank - _TC"
# Create journal entries WITHOUT any prior Period Closing Voucher
# This ensures the account exists in gl_dict but NOT in balances_data
jv = make_journal_entry(
account1=cash_account,
account2=bank_account,
amount=2500,
posting_date="2024-07-15",
company=company,
submit=True,
)
try:
# Set up filters - use a period with no prior PCV
filters = {
"company": company,
"from_fiscal_year": "2024",
"to_fiscal_year": "2024",
"period_start_date": "2024-07-01",
"period_end_date": "2024-09-30",
"filter_based_on": "Date Range",
"periodicity": "Monthly",
}
periods = [
{"key": "2024_jul", "from_date": "2024-07-01", "to_date": "2024-07-31"},
{"key": "2024_aug", "from_date": "2024-08-01", "to_date": "2024-08-31"},
{"key": "2024_sep", "from_date": "2024-09-01", "to_date": "2024-09-30"},
]
query_builder = FinancialQueryBuilder(filters, periods)
# Use accounts that have GL entries but may not have Account Closing Balance
accounts = [
frappe._dict({"name": cash_account, "account_name": "Cash", "account_number": "1001"}),
frappe._dict({"name": bank_account, "account_name": "Bank", "account_number": "1002"}),
]
balances_data = query_builder.fetch_account_balances(accounts)
# Verify accounts are present in results even without prior closing balance
cash_data = balances_data.get(cash_account)
self.assertIsNotNone(cash_data, "Cash account should exist in results")
bank_data = balances_data.get(bank_account)
self.assertIsNotNone(bank_data, "Bank account should exist in results")
# Verify July has the movement from journal entry
jul_cash = cash_data.get_period("2024_jul")
self.assertIsNotNone(jul_cash, "July period should exist for cash")
self.assertEqual(jul_cash.movement, 2500.0, "July cash movement should be 2500")
jul_bank = bank_data.get_period("2024_jul")
self.assertIsNotNone(jul_bank, "July period should exist for bank")
self.assertEqual(jul_bank.movement, -2500.0, "July bank movement should be -2500")
# Verify subsequent periods exist with zero movement
aug_cash = cash_data.get_period("2024_aug")
self.assertIsNotNone(aug_cash, "August period should exist for cash")
self.assertEqual(aug_cash.movement, 0.0, "August cash movement should be 0")
self.assertEqual(aug_cash.opening, jul_cash.closing, "August opening = July closing")
sep_cash = cash_data.get_period("2024_sep")
self.assertIsNotNone(sep_cash, "September period should exist for cash")
self.assertEqual(sep_cash.movement, 0.0, "September cash movement should be 0")
self.assertEqual(sep_cash.opening, aug_cash.closing, "September opening = August closing")
finally:
jv.cancel()

View File

@@ -4,7 +4,7 @@
import frappe
from dateutil.relativedelta import relativedelta
from frappe import _
from frappe import _, cint
from frappe.model.document import Document
from frappe.utils import add_days, add_years, cstr, getdate
@@ -33,23 +33,11 @@ class FiscalYear(Document):
self.validate_dates()
self.validate_overlap()
if not self.is_new():
year_start_end_dates = frappe.db.sql(
"""select year_start_date, year_end_date
from `tabFiscal Year` where name=%s""",
(self.name),
)
def on_update(self):
frappe.cache().delete_key("fiscal_years")
if year_start_end_dates:
if (
getdate(self.year_start_date) != year_start_end_dates[0][0]
or getdate(self.year_end_date) != year_start_end_dates[0][1]
):
frappe.throw(
_(
"Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."
)
)
def on_trash(self):
frappe.cache().delete_key("fiscal_years")
def validate_dates(self):
self.validate_from_to_dates("year_start_date", "year_end_date")
@@ -66,28 +54,20 @@ class FiscalYear(Document):
frappe.exceptions.InvalidDates,
)
def on_update(self):
check_duplicate_fiscal_year(self)
frappe.cache().delete_value("fiscal_years")
def on_trash(self):
frappe.cache().delete_value("fiscal_years")
def validate_overlap(self):
existing_fiscal_years = frappe.db.sql(
"""select name from `tabFiscal Year`
where (
(%(year_start_date)s between year_start_date and year_end_date)
or (%(year_end_date)s between year_start_date and year_end_date)
or (year_start_date between %(year_start_date)s and %(year_end_date)s)
or (year_end_date between %(year_start_date)s and %(year_end_date)s)
) and name!=%(name)s""",
{
"year_start_date": self.year_start_date,
"year_end_date": self.year_end_date,
"name": self.name or "No Name",
},
as_dict=True,
fy = frappe.qb.DocType("Fiscal Year")
name = self.name or self.year
existing_fiscal_years = (
frappe.qb.from_(fy)
.select(fy.name)
.where(
(fy.year_start_date <= self.year_end_date)
& (fy.year_end_date >= self.year_start_date)
& (fy.name != name)
)
.run(as_dict=True)
)
if existing_fiscal_years:
@@ -110,37 +90,30 @@ class FiscalYear(Document):
frappe.throw(
_(
"Year start date or end date is overlapping with {0}. To avoid please set company"
).format(existing.name),
).format(frappe.get_desk_link("Fiscal Year", existing.name, open_in_new_tab=True)),
frappe.NameError,
)
@frappe.whitelist()
def check_duplicate_fiscal_year(doc):
year_start_end_dates = frappe.db.sql(
"""select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""",
(doc.name),
)
for fiscal_year, ysd, yed in year_start_end_dates:
if (getdate(doc.year_start_date) == ysd and getdate(doc.year_end_date) == yed) and (
not frappe.in_test
):
frappe.throw(
_(
"Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}"
).format(fiscal_year)
)
@frappe.whitelist()
def auto_create_fiscal_year():
for d in frappe.db.sql(
"""select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)"""
):
fy = frappe.qb.DocType("Fiscal Year")
# Skipped auto-creating Short Year, as it has very rare use case.
# Reference: https://www.irs.gov/businesses/small-businesses-self-employed/tax-years (US)
follow_up_date = add_days(getdate(), days=3)
fiscal_year = (
frappe.qb.from_(fy)
.select(fy.name)
.where((fy.year_end_date == follow_up_date) & (fy.is_short_year == 0))
.run()
)
for d in fiscal_year:
try:
current_fy = frappe.get_doc("Fiscal Year", d[0])
new_fy = frappe.copy_doc(current_fy, ignore_no_copy=False)
new_fy = frappe.new_doc("Fiscal Year")
new_fy.disabled = cint(current_fy.disabled)
new_fy.year_start_date = add_days(current_fy.year_end_date, 1)
new_fy.year_end_date = add_years(current_fy.year_end_date, 1)
@@ -148,6 +121,10 @@ def auto_create_fiscal_year():
start_year = cstr(new_fy.year_start_date.year)
end_year = cstr(new_fy.year_end_date.year)
new_fy.year = start_year if start_year == end_year else (start_year + "-" + end_year)
for row in current_fy.companies:
new_fy.append("companies", {"company": row.company})
new_fy.auto_created = 1
new_fy.insert(ignore_permissions=True)

View File

@@ -15,20 +15,22 @@
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Company",
"options": "Company"
"options": "Company",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-03-27 13:09:44.659251",
"modified": "2026-02-20 23:02:26.193606",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Fiscal Year Company",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -14,7 +14,7 @@ class FiscalYearCompany(Document):
if TYPE_CHECKING:
from frappe.types import DF
company: DF.Link | None
company: DF.Link
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data

View File

@@ -317,7 +317,7 @@ class InvoiceDiscounting(AccountsController):
@frappe.whitelist()
def get_invoices(filters):
def get_invoices(filters: str):
filters = frappe._dict(json.loads(filters))
cond = []
if filters.customer:

View File

@@ -277,7 +277,21 @@ frappe.ui.form.on("Journal Entry", {
var update_jv_details = function (doc, r) {
$.each(r, function (i, d) {
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
frappe.model.set_value(row.doctype, row.name, "account", d.account);
const {
idx,
name,
owner,
parent,
parenttype,
parentfield,
creation,
modified,
modified_by,
doctype,
docstatus,
...fields
} = d;
frappe.model.set_value(row.doctype, row.name, fields);
});
refresh_field("accounts");
};
@@ -289,10 +303,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
}
onload_post_render() {
this.frm.get_field("accounts").grid.set_multiple_add("account");
}
load_defaults() {
//this.frm.show_print_first = true;
if (this.frm.doc.__islocal && this.frm.doc.company) {

View File

@@ -9,19 +9,16 @@
"engine": "InnoDB",
"field_order": [
"entry_type_and_date",
"is_system_generated",
"title",
"voucher_type",
"naming_series",
"process_deferred_accounting",
"reversal_of",
"column_break1",
"from_template",
"company",
"voucher_type",
"column_break1",
"naming_series",
"posting_date",
"finance_book",
"multi_currency",
"apply_tds",
"tax_withholding_category",
"is_system_generated",
"amended_from",
"section_break_tcvw",
"for_all_stock_asset_accounts",
"column_break_wpau",
@@ -30,52 +27,60 @@
"get_balance_for_periodic_accounting",
"2_add_edit_gl_entries",
"accounts",
"section_break99",
"cheque_no",
"cheque_date",
"user_remark",
"column_break99",
"section_break_ouaq",
"total_debit",
"column_break_cixu",
"total_credit",
"difference",
"get_balance",
"multi_currency",
"total_amount_currency",
"total_amount",
"total_amount_in_words",
"section_break99",
"cheque_no",
"cheque_date",
"clearance_date",
"column_break_oizh",
"user_remark",
"subscription_section",
"auto_repeat",
"tax_withholding_tab",
"section_tax_withholding_entry",
"tax_withholding_group",
"ignore_tax_withholding_threshold",
"override_tax_withholding_entries",
"tax_withholding_entries",
"more_info_tab",
"reference",
"clearance_date",
"remark",
"inter_company_journal_entry_reference",
"column_break98",
"bill_no",
"bill_date",
"due_date",
"column_break_isfa",
"inter_company_journal_entry_reference",
"process_deferred_accounting",
"reversal_of",
"payment_order",
"stock_entry",
"printing_settings",
"pay_to_recd_from",
"letter_head",
"select_print_heading",
"column_break_35",
"total_amount_currency",
"total_amount",
"total_amount_in_words",
"write_off",
"write_off_based_on",
"get_outstanding_invoices",
"column_break_30",
"write_off_amount",
"printing_settings",
"pay_to_recd_from",
"column_break_35",
"letter_head",
"select_print_heading",
"addtional_info",
"mode_of_payment",
"payment_order",
"party_not_required",
"column_break3",
"is_opening",
"stock_entry",
"subscription_section",
"auto_repeat",
"amended_from"
"finance_book",
"from_template",
"title",
"column_break3",
"remark",
"mode_of_payment",
"party_not_required"
],
"fields": [
{
@@ -155,6 +160,7 @@
{
"fieldname": "2_add_edit_gl_entries",
"fieldtype": "Section Break",
"hide_border": 1,
"oldfieldtype": "Section Break",
"options": "fa fa-table"
},
@@ -202,10 +208,6 @@
"oldfieldtype": "Small Text",
"print_hide": 1
},
{
"fieldname": "column_break99",
"fieldtype": "Column Break"
},
{
"fieldname": "total_debit",
"fieldtype": "Currency",
@@ -429,7 +431,7 @@
"collapsible": 1,
"fieldname": "addtional_info",
"fieldtype": "Section Break",
"label": "More Information",
"label": "Additional Info",
"oldfieldtype": "Section Break",
"options": "fa fa-file-text"
},
@@ -476,7 +478,7 @@
{
"fieldname": "subscription_section",
"fieldtype": "Section Break",
"label": "Subscription Section"
"label": "Subscription"
},
{
"allow_on_submit": 1,
@@ -593,12 +595,10 @@
"no_copy": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "eval: doc.apply_tds && doc.docstatus == 0",
"depends_on": "eval: doc.apply_tds",
"fieldname": "section_tax_withholding_entry",
"fieldtype": "Section Break",
"label": "Tax Withholding Entry"
"fieldtype": "Section Break"
},
{
"fieldname": "tax_withholding_group",
@@ -624,6 +624,33 @@
"label": "Tax Withholding Entries",
"options": "Tax Withholding Entry",
"read_only_depends_on": "eval: !doc.override_tax_withholding_entries"
},
{
"fieldname": "more_info_tab",
"fieldtype": "Tab Break",
"label": "More Info"
},
{
"fieldname": "section_break_ouaq",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_cixu",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_oizh",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_isfa",
"fieldtype": "Column Break"
},
{
"depends_on": "eval: doc.apply_tds",
"fieldname": "tax_withholding_tab",
"fieldtype": "Tab Break",
"label": "Tax Withholding"
}
],
"icon": "fa fa-file-text",
@@ -638,7 +665,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2025-11-13 17:54:14.542903",
"modified": "2026-02-16 16:06:10.468482",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@@ -1,12 +1,13 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import json
from datetime import date
import frappe
from frappe import _, msgprint, scrub
from frappe.core.doctype.submission_queue.submission_queue import queue_submission
from frappe.model.document import Document
from frappe.utils import comma_and, cstr, flt, fmt_money, formatdate, get_link_to_form, nowdate
import erpnext
@@ -74,8 +75,8 @@ class JournalEntry(AccountsController):
mode_of_payment: DF.Link | None
multi_currency: DF.Check
naming_series: DF.Literal["ACC-JV-.YYYY.-"]
party_not_required: DF.Check
override_tax_withholding_entries: DF.Check
party_not_required: DF.Check
pay_to_recd_from: DF.Data | None
payment_order: DF.Link | None
periodic_entry_difference_account: DF.Link | None
@@ -179,7 +180,7 @@ class JournalEntry(AccountsController):
validate_docs_for_deferred_accounting([self.name], [])
def submit(self):
if len(self.accounts) > 100:
if len(self.accounts) > 100 and not self.meta.queue_in_background:
queue_submission(self, "_submit")
else:
return self._submit()
@@ -1215,7 +1216,7 @@ class JournalEntry(AccountsController):
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
@frappe.whitelist()
def get_balance(self, difference_account=None):
def get_balance(self, difference_account: str | None = None):
if not self.get("accounts"):
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
else:
@@ -1321,7 +1322,12 @@ class JournalEntry(AccountsController):
@frappe.whitelist()
def get_default_bank_cash_account(
company, account_type=None, mode_of_payment=None, account=None, *, fetch_balance=True
company: str,
account_type: str | None = None,
mode_of_payment: str | None = None,
account: str | None = None,
*,
fetch_balance: bool = True,
):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
@@ -1370,7 +1376,12 @@ def get_default_bank_cash_account(
@frappe.whitelist()
def get_payment_entry_against_order(
dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None
dt: str,
dn: str,
amount: float | None = None,
debit_in_account_currency: str | float | None = None,
journal_entry: bool = False,
bank_account: str | None = None,
):
ref_doc = frappe.get_doc(dt, dn)
@@ -1415,7 +1426,12 @@ def get_payment_entry_against_order(
@frappe.whitelist()
def get_payment_entry_against_invoice(
dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None
dt: str,
dn: str,
amount: float | None = None,
debit_in_account_currency: str | None = None,
journal_entry: bool = False,
bank_account: str | None = None,
):
ref_doc = frappe.get_doc(dt, dn)
if dt == "Sales Invoice":
@@ -1528,7 +1544,7 @@ def get_payment_entry(ref_doc, args):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
def get_against_jv(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
if not frappe.db.has_column("Journal Entry", searchfield):
return []
@@ -1559,7 +1575,7 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
def get_outstanding(args):
def get_outstanding(args: str | dict):
if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1)
@@ -1619,7 +1635,7 @@ def get_outstanding(args):
@frappe.whitelist()
def get_party_account_and_currency(company, party_type, party):
def get_party_account_and_currency(company: str, party_type: str, party: str):
if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1)
@@ -1632,7 +1648,14 @@ def get_party_account_and_currency(company, party_type, party):
@frappe.whitelist()
def get_account_details_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None):
def get_account_details_and_party_type(
account: str,
date: str,
company: str,
debit: float | str | None = None,
credit: float | str | None = None,
exchange_rate: float | str | None = None,
):
"""Returns dict of account details and party type to be set in Journal Entry on selection of account."""
if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1)
@@ -1681,16 +1704,20 @@ def get_account_details_and_party_type(account, date, company, debit=None, credi
@frappe.whitelist()
def get_exchange_rate(
posting_date,
account=None,
account_currency=None,
company=None,
reference_type=None,
reference_name=None,
debit=None,
credit=None,
exchange_rate=None,
posting_date: str | date,
account: str | None = None,
account_currency: str | None = None,
company: str | None = None,
reference_type: str | None = None,
reference_name: str | None = None,
debit: float | str | None = None,
credit: float | str | None = None,
exchange_rate: str | float | None = None,
):
# Ensure exchange_rate is always numeric to avoid calculation errors
if isinstance(exchange_rate, str):
exchange_rate = flt(exchange_rate) or 1
account_details = frappe.get_cached_value(
"Account", account, ["account_type", "root_type", "account_currency", "company"], as_dict=1
)
@@ -1722,7 +1749,7 @@ def get_exchange_rate(
@frappe.whitelist()
def get_average_exchange_rate(account):
def get_average_exchange_rate(account: str):
exchange_rate = 0
bank_balance_in_account_currency = get_balance_on(account)
if bank_balance_in_account_currency:
@@ -1733,7 +1760,7 @@ def get_average_exchange_rate(account):
@frappe.whitelist()
def make_inter_company_journal_entry(name, voucher_type, company):
def make_inter_company_journal_entry(name: str, voucher_type: str, company: str):
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = voucher_type
journal_entry.company = company
@@ -1743,7 +1770,7 @@ def make_inter_company_journal_entry(name, voucher_type, company):
@frappe.whitelist()
def make_reverse_journal_entry(source_name, target_doc=None):
def make_reverse_journal_entry(source_name: str, target_doc: str | Document | None = None):
existing_reverse = frappe.db.exists("Journal Entry", {"reversal_of": source_name, "docstatus": 1})
if existing_reverse:
frappe.throw(

View File

@@ -43,7 +43,7 @@
"fields": [
{
"bold": 1,
"columns": 2,
"columns": 4,
"fieldname": "account",
"fieldtype": "Link",
"in_global_search": 1,
@@ -185,20 +185,19 @@
"fieldtype": "Select",
"label": "Reference Type",
"no_copy": 1,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry",
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry\nBank Transaction",
"search_index": 1
},
{
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Reference Name",
"no_copy": 1,
"options": "reference_type",
"search_index": 1
},
{
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance', 'Bank Transaction'])",
"fieldname": "reference_due_date",
"fieldtype": "Date",
"label": "Reference Due Date",
@@ -294,7 +293,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2025-11-27 12:23:33.157655",
"modified": "2026-02-19 17:01:22.642454",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

View File

@@ -55,6 +55,7 @@ class JournalEntryAccount(Document):
"Fees",
"Full and Final Statement",
"Payment Entry",
"Bank Transaction",
]
user_remark: DF.SmallText | None
# end: auto-generated types

View File

@@ -3,6 +3,7 @@
frappe.ui.form.on("Journal Entry Template", {
onload: function (frm) {
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
if (frm.is_new()) {
frappe.call({
type: "GET",
@@ -37,6 +38,31 @@ frappe.ui.form.on("Journal Entry Template", {
return { filters: filters };
});
frm.set_query("project", "accounts", function (doc, cdt, cdn) {
let row = frappe.get_doc(cdt, cdn);
let filters = {
company: doc.company,
};
if (row.party_type == "Customer") {
filters.customer = row.party;
}
return {
query: "erpnext.controllers.queries.get_project_name",
filters,
};
});
frm.set_query("party_type", "accounts", function (doc, cdt, cdn) {
const row = locals[cdt][cdn];
return {
query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
filters: {
account: row.account,
},
};
});
},
voucher_type: function (frm) {
var add_accounts = function (doc, r) {

View File

@@ -3,6 +3,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
@@ -42,7 +43,29 @@ class JournalEntryTemplate(Document):
]
# end: auto-generated types
pass
def validate(self):
self.validate_party()
def validate_party(self):
"""
Loop over all accounts and see if party and party type is set correctly
"""
for account in self.accounts:
if account.party_type:
account_type = frappe.get_cached_value("Account", account.account, "account_type")
if account_type not in ["Receivable", "Payable"]:
frappe.throw(
_(
"Check row {0} for account {1}: Party Type is only allowed for Receivable or Payable accounts"
).format(account.idx, account.account)
)
if account.party and not account.party_type:
frappe.throw(
_("Check row {0} for account {1}: Party is only allowed if Party Type is set").format(
account.idx, account.account
)
)
@frappe.whitelist()

View File

@@ -5,7 +5,13 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"account"
"account",
"party_type",
"party",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project"
],
"fields": [
{
@@ -15,18 +21,55 @@
"label": "Account",
"options": "Account",
"reqd": 1
},
{
"fieldname": "party_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Party Type",
"options": "DocType"
},
{
"fieldname": "party",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Party",
"options": "party_type"
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
}
],
"istable": 1,
"links": [],
"modified": "2024-03-27 13:09:58.986448",
"modified": "2026-01-09 13:16:27.615083",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Template Account",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -16,9 +16,13 @@ class JournalEntryTemplateAccount(Document):
from frappe.types import DF
account: DF.Link
cost_center: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
party: DF.DynamicLink | None
party_type: DF.Link | None
project: DF.Link | None
# end: auto-generated types
pass

View File

@@ -55,7 +55,7 @@ class LedgerMerge(Document):
@frappe.whitelist()
def form_start_merge(docname):
def form_start_merge(docname: str):
return frappe.get_doc("Ledger Merge", docname).start_merge()

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from datetime import date
import frappe
from frappe import _
@@ -88,13 +89,13 @@ def get_loyalty_details(
@frappe.whitelist()
def get_loyalty_program_details_with_points(
customer,
loyalty_program=None,
expiry_date=None,
company=None,
silent=False,
include_expired_entry=False,
current_transaction_amount=0,
customer: str,
loyalty_program: str | None = None,
expiry_date: str | date | None = None,
company: str | None = None,
silent: bool = False,
include_expired_entry: bool = False,
current_transaction_amount: int | float = 0,
):
lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
@@ -119,12 +120,12 @@ def get_loyalty_program_details_with_points(
@frappe.whitelist()
def get_loyalty_program_details(
customer,
loyalty_program=None,
expiry_date=None,
company=None,
silent=False,
include_expired_entry=False,
customer: str,
loyalty_program: str | None = None,
expiry_date: str | date | None = None,
company: str | None = None,
silent: bool = False,
include_expired_entry: bool = False,
):
lp_details = frappe._dict()
@@ -146,7 +147,7 @@ def get_loyalty_program_details(
@frappe.whitelist()
def get_redeemption_factor(loyalty_program=None, customer=None):
def get_redeemption_factor(loyalty_program: str | None = None, customer: str | None = None):
customer_loyalty_program = None
if not loyalty_program:
customer_loyalty_program = frappe.db.get_value("Customer", customer, "loyalty_program")

View File

@@ -293,7 +293,7 @@ def publish(index, total, doctype):
@frappe.whitelist()
def get_temporary_opening_account(company=None):
def get_temporary_opening_account(company: str | None = None):
if not company:
return

View File

@@ -67,7 +67,7 @@ class PartyLink(Document):
@frappe.whitelist()
def create_party_link(primary_role, primary_party, secondary_party):
def create_party_link(primary_role: str, primary_party: str, secondary_party: str):
party_link = frappe.new_doc("Party Link")
party_link.primary_role = primary_role
party_link.primary_party = primary_party

View File

@@ -400,6 +400,16 @@ frappe.ui.form.on("Payment Entry", {
);
frm.refresh_fields();
const party_currency =
frm.doc.payment_type === "Receive" ? "paid_from_account_currency" : "paid_to_account_currency";
var reference_grid = frm.fields_dict["references"].grid;
["total_amount", "outstanding_amount", "allocated_amount"].forEach((fieldname) => {
reference_grid.update_docfield_property(fieldname, "options", party_currency);
});
reference_grid.refresh();
},
show_general_ledger: function (frm) {
@@ -502,12 +512,16 @@ frappe.ui.form.on("Payment Entry", {
frm.set_value("contact_email", "");
frm.set_value("contact_person", "");
}
if (frm.doc.payment_type && frm.doc.party_type && frm.doc.party && frm.doc.company) {
if (!frm.doc.posting_date) {
frappe.msgprint(__("Please select Posting Date before selecting Party"));
frm.set_value("party", "");
return;
}
erpnext.utils.get_employee_contact_details(frm);
frm.set_party_account_based_on_party = true;
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
@@ -1104,7 +1118,7 @@ frappe.ui.form.on("Payment Entry", {
allocate_party_amount_against_ref_docs: async function (frm, paid_amount, paid_amount_change) {
await frm.call("allocate_amount_to_references", {
paid_amount: paid_amount,
paid_amount: flt(paid_amount),
paid_amount_change: paid_amount_change,
allocate_payment_amount: frappe.flags.allocate_payment_amount ?? false,
});
@@ -1440,16 +1454,15 @@ frappe.ui.form.on("Payment Entry", {
callback: function (r) {
if (!r.exc && r.message) {
// set taxes table
if (r.message) {
for (let tax of r.message) {
if (tax.charge_type === "On Net Total") {
tax.charge_type = "On Paid Amount";
}
frm.add_child("taxes", tax);
let taxes = r.message;
taxes.forEach((tax) => {
if (tax.charge_type === "On Net Total") {
tax.charge_type = "On Paid Amount";
}
frm.events.apply_taxes(frm);
frm.events.set_unallocated_amount(frm);
}
});
frm.set_value("taxes", taxes);
frm.events.apply_taxes(frm);
frm.events.set_unallocated_amount(frm);
}
},
});

View File

@@ -701,7 +701,6 @@
"fetch_from": "company.book_advance_payments_in_separate_party_account",
"fieldname": "book_advance_payments_in_separate_party_account",
"fieldtype": "Check",
"hidden": 1,
"label": "Book Advance Payments in Separate Party Account",
"no_copy": 1,
"read_only": 1
@@ -793,7 +792,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2025-12-18 13:56:40.206038",
"modified": "2026-02-03 16:08:49.800381",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -1,12 +1,13 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
from datetime import date
from functools import reduce
import frappe
from frappe import ValidationError, _, qb, scrub, throw
from frappe.model.document import Document
from frappe.model.meta import get_field_precision
from frappe.query_builder import Tuple
from frappe.query_builder.functions import Count
@@ -1064,8 +1065,12 @@ class PaymentEntry(AccountsController):
total_allocated_amount += flt(d.allocated_amount)
base_total_allocated_amount += self.calculate_base_allocated_amount_for_reference(d)
self.total_allocated_amount = abs(total_allocated_amount)
self.base_total_allocated_amount = abs(base_total_allocated_amount)
self.total_allocated_amount = flt(
abs(total_allocated_amount), self.precision("total_allocated_amount")
)
self.base_total_allocated_amount = flt(
abs(base_total_allocated_amount), self.precision("base_total_allocated_amount")
)
def set_unallocated_amount(self):
self.unallocated_amount = 0
@@ -1081,20 +1086,32 @@ class PaymentEntry(AccountsController):
self.base_paid_amount + deductions_to_consider
):
self.unallocated_amount = (
self.base_paid_amount
+ deductions_to_consider
- self.base_total_allocated_amount
- included_taxes
) / self.source_exchange_rate
flt(
(
self.base_paid_amount
+ deductions_to_consider
- self.base_total_allocated_amount
- included_taxes
),
self.precision("unallocated_amount"),
)
/ self.source_exchange_rate
)
elif self.payment_type == "Pay" and self.base_total_allocated_amount < (
self.base_received_amount - deductions_to_consider
):
self.unallocated_amount = (
self.base_received_amount
- deductions_to_consider
- self.base_total_allocated_amount
- included_taxes
) / self.target_exchange_rate
flt(
(
self.base_received_amount
- deductions_to_consider
- self.base_total_allocated_amount
- included_taxes
),
self.precision("unallocated_amount"),
)
/ self.target_exchange_rate
)
def set_exchange_gain_loss(self):
exchange_gain_loss = flt(
@@ -1867,7 +1884,9 @@ class PaymentEntry(AccountsController):
frappe.response["matched_payment_requests"] = matched_payment_requests
@frappe.whitelist()
def allocate_amount_to_references(self, paid_amount, paid_amount_change, allocate_payment_amount):
def allocate_amount_to_references(
self, paid_amount: float, paid_amount_change: bool, allocate_payment_amount: bool
):
"""
Allocate `Allocated Amount` and `Payment Request` against `Reference` based on `Paid Amount` and `Outstanding Amount`.\n
:param paid_amount: Paid Amount / Received Amount.
@@ -2039,7 +2058,7 @@ class PaymentEntry(AccountsController):
)
@frappe.whitelist()
def set_matched_payment_requests(self, matched_payment_requests):
def set_matched_payment_requests(self, matched_payment_requests: str | list | None):
"""
Set `Payment Request` against `Reference` based on `matched_payment_requests`.\n
:param matched_payment_requests: List of tuple of matched Payment Requests.
@@ -2255,7 +2274,7 @@ def validate_inclusive_tax(tax, doc):
@frappe.whitelist()
def get_outstanding_reference_documents(args, validate=False):
def get_outstanding_reference_documents(args: str | dict, validate: bool = False):
if isinstance(args, str):
args = json.loads(args)
@@ -2670,7 +2689,7 @@ def get_negative_outstanding_invoices(
@frappe.whitelist()
def get_party_details(company, party_type, party, date, cost_center=None):
def get_party_details(company: str, party_type: str, party: str, date: str, cost_center: str | None = None):
bank_account = ""
party_bank_account = ""
@@ -2696,7 +2715,7 @@ def get_party_details(company, party_type, party, date, cost_center=None):
@frappe.whitelist()
def get_account_details(account, date, cost_center=None):
def get_account_details(account: str, date: str | date, cost_center: str | None = None):
frappe.has_permission("Payment Entry", throw=True)
# to check if the passed account is accessible under reference doctype Payment Entry
@@ -2716,7 +2735,7 @@ def get_account_details(account, date, cost_center=None):
@frappe.whitelist()
def get_company_defaults(company):
def get_company_defaults(company: str):
fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"]
return frappe.get_cached_value("Company", company, fields, as_dict=1)
@@ -2755,7 +2774,11 @@ def get_outstanding_on_journal_entry(voucher_no, party_type, party):
@frappe.whitelist()
def get_reference_details(
reference_doctype, reference_name, party_account_currency, party_type=None, party=None
reference_doctype: str,
reference_name: str,
party_account_currency: str,
party_type: str | None = None,
party: str | None = None,
):
total_amount = outstanding_amount = exchange_rate = account = None
@@ -2846,15 +2869,15 @@ def get_reference_details(
@frappe.whitelist()
def get_payment_entry(
dt,
dn,
party_amount=None,
bank_account=None,
bank_amount=None,
party_type=None,
payment_type=None,
reference_date=None,
created_from_payment_request=False,
dt: str,
dn: str,
party_amount: int | float | None = None,
bank_account: str | None = None,
bank_amount: int | float | None = None,
party_type: str | None = None,
payment_type: str | None = None,
reference_date: str | date | None = None,
created_from_payment_request: bool | None = None,
):
doc = frappe.get_doc(dt, dn)
over_billing_allowance = frappe.get_single_value("Accounts Settings", "over_billing_allowance")
@@ -3520,7 +3543,7 @@ def get_paid_amount(dt, dn, party_type, party, account, due_date):
@frappe.whitelist()
def make_payment_order(source_name, target_doc=None):
def make_payment_order(source_name: str, target_doc: str | Document | None = None):
from frappe.model.mapper import get_mapped_doc
def set_missing_values(source, target):

View File

@@ -132,6 +132,12 @@
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"fieldname": "due_date",

View File

@@ -38,6 +38,7 @@ class PaymentLedgerEntry(Document):
amount_in_account_currency: DF.Currency
company: DF.Link | None
cost_center: DF.Link | None
project: DF.Link | None
delinked: DF.Check
due_date: DF.Date | None
finance_book: DF.Link | None

View File

@@ -59,7 +59,7 @@ class PaymentOrder(Document):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
def get_mop_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
return frappe.db.sql(
""" select mode_of_payment from `tabPayment Order Reference`
where parent = %(parent)s and mode_of_payment like %(txt)s
@@ -70,7 +70,7 @@ def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_supplier_query(doctype, txt, searchfield, start, page_len, filters):
def get_supplier_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
return frappe.db.sql(
""" select supplier from `tabPayment Order Reference`
where parent = %(parent)s and supplier like %(txt)s and
@@ -81,7 +81,7 @@ def get_supplier_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
def make_payment_records(name, supplier, mode_of_payment=None):
def make_payment_records(name: str, supplier: str, mode_of_payment: str | None = None):
doc = frappe.get_doc("Payment Order", name)
make_journal_entry(doc, supplier, mode_of_payment)

View File

@@ -433,7 +433,9 @@ class PaymentReconciliation(Document):
return frappe.get_single_value("Accounts Settings", "auto_reconcile_payments")
@frappe.whitelist()
def calculate_difference_on_allocation_change(self, payment_entry, invoice, allocated_amount):
def calculate_difference_on_allocation_change(
self, payment_entry: list, invoice: list, allocated_amount: float
):
invoice_exchange_map = self.get_invoice_exchange_map(invoice, payment_entry)
invoice[0]["exchange_rate"] = invoice_exchange_map.get(invoice[0].get("invoice_number"))
if payment_entry[0].get("reference_type") in ["Sales Invoice", "Purchase Invoice"]:
@@ -445,7 +447,7 @@ class PaymentReconciliation(Document):
return new_difference_amount
@frappe.whitelist()
def allocate_entries(self, args):
def allocate_entries(self, args: dict):
self.validate_entries()
exc_gain_loss_posting_date = frappe.db.get_single_value(
@@ -746,7 +748,7 @@ class PaymentReconciliation(Document):
ple = qb.DocType("Payment Ledger Entry")
for x in self.dimensions:
dimension = x.fieldname
if self.get(dimension):
if self.get(dimension) and frappe.db.has_column("Payment Ledger Entry", dimension):
self.accounting_dimension_filter_conditions.append(ple[dimension] == self.get(dimension))
def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):

View File

@@ -0,0 +1,88 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2025-12-02 17:50:08.648006",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"payment_term",
"column_break_lnjp",
"payment_schedule",
"section_break_fjhh",
"description",
"section_break_mjlv",
"due_date",
"column_break_qghl",
"amount"
],
"fields": [
{
"fieldname": "payment_term",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Payment Term",
"options": "Payment Term"
},
{
"collapsible": 1,
"fieldname": "section_break_fjhh",
"fieldtype": "Section Break",
"label": "Description"
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"in_list_view": 1,
"label": "Description"
},
{
"fieldname": "section_break_mjlv",
"fieldtype": "Section Break"
},
{
"fieldname": "due_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Due Date"
},
{
"fieldname": "column_break_qghl",
"fieldtype": "Column Break"
},
{
"fieldname": "amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Amount",
"precision": "2"
},
{
"fieldname": "column_break_lnjp",
"fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "payment_schedule",
"fieldtype": "Link",
"label": "Payment Schedule",
"options": "Payment Schedule",
"read_only": 1
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2026-01-19 02:21:36.455830",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reference",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"rows_threshold_for_grid_search": 20,
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -0,0 +1,27 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class PaymentReference(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
amount: DF.Currency
description: DF.SmallText | None
due_date: DF.Date | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
payment_schedule: DF.Link | None
payment_term: DF.Link | None
# end: auto-generated types
pass

View File

@@ -105,3 +105,29 @@ frappe.ui.form.on("Payment Request", "is_a_subscription", function (frm) {
});
}
});
frappe.ui.form.on("Payment Request", "calculate_total_amount_by_selected_rows", function (frm) {
if (frm.doc.docstatus !== 0) {
frappe.msgprint(__("Cannot fetch selected rows for submitted Payment Request"));
return;
}
const selected = frm.get_selected()?.payment_reference || [];
if (!selected.length) {
frappe.throw(__("No rows selected"));
}
let total = 0;
selected.forEach((name) => {
const row = frm.doc.payment_reference.find((d) => d.name === name);
if (row) {
row.manually_selected = 1;
total += row.amount;
}
});
frm.doc.payment_reference.forEach((row) => {
row.auto_selected = 0;
});
frm.set_value("grand_total", total);
frm.refresh_field("grand_total");
frm.save();
});

View File

@@ -19,6 +19,8 @@
"column_break_4",
"reference_doctype",
"reference_name",
"payment_reference_section",
"payment_reference",
"transaction_details",
"grand_total",
"currency",
@@ -157,6 +159,7 @@
"label": "Amount",
"non_negative": 1,
"options": "currency",
"read_only_depends_on": "eval:doc.payment_reference.length>0",
"reqd": 1
},
{
@@ -457,6 +460,17 @@
"fieldname": "phone_number",
"fieldtype": "Data",
"label": "Phone Number"
},
{
"fieldname": "payment_reference_section",
"fieldtype": "Section Break"
},
{
"fieldname": "payment_reference",
"fieldtype": "Table",
"label": "Payment Reference",
"options": "Payment Reference",
"read_only": 1
}
],
"grid_page_length": 50,
@@ -464,7 +478,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2025-08-29 11:52:48.555415",
"modified": "2026-01-13 12:53:00.963274",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",

View File

@@ -45,6 +45,7 @@ class PaymentRequest(Document):
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.payment_reference.payment_reference import PaymentReference
from erpnext.accounts.doctype.subscription_plan_detail.subscription_plan_detail import (
SubscriptionPlanDetail,
)
@@ -78,6 +79,7 @@ class PaymentRequest(Document):
payment_gateway: DF.ReadOnly | None
payment_gateway_account: DF.Link | None
payment_order: DF.Link | None
payment_reference: DF.Table[PaymentReference]
payment_request_type: DF.Literal["Outward", "Inward"]
payment_url: DF.Data | None
phone_number: DF.Data | None
@@ -109,15 +111,36 @@ class PaymentRequest(Document):
if self.get("__islocal"):
self.status = "Draft"
self.validate_reference_document()
self.validate_against_payment_reference()
self.validate_payment_request_amount()
# self.validate_currency()
self.validate_subscription_details()
def validate_against_payment_reference(self):
if not self.payment_reference:
return
expected = sum(flt(r.amount) for r in self.payment_reference)
if flt(expected, self.precision("grand_total")) != flt(self.grand_total):
frappe.throw(_("Grand Total must match sum of Payment References"))
seen = set()
for r in self.payment_reference:
if not r.payment_schedule:
continue # legacy mode → skip
if r.payment_schedule in seen:
frappe.throw(_("Duplicate Payment Schedule selected"))
seen.add(r.payment_schedule)
def validate_reference_document(self):
if not self.reference_doctype or not self.reference_name:
frappe.throw(_("To create a Payment Request reference document is required"))
def validate_payment_request_amount(self):
if self.payment_reference:
return
if self.grand_total == 0:
frappe.throw(
_("{0} cannot be zero").format(self.get_label_from_fieldname("grand_total")),
@@ -535,7 +558,7 @@ class PaymentRequest(Document):
row_number += TO_SKIP_NEW_ROW
@frappe.whitelist(allow_guest=True)
@frappe.whitelist()
def make_payment_request(**args):
"""Make payment request"""
@@ -546,12 +569,69 @@ def make_payment_request(**args):
if args.dn and not isinstance(args.dn, str):
frappe.throw(_("Invalid parameter. 'dn' should be of type str"))
frappe.has_permission("Payment Request", "create", throw=True)
frappe.has_permission(args.dt, "read", args.dn, throw=True)
ref_doc = args.ref_doc or frappe.get_doc(args.dt, args.dn)
if not args.get("company"):
args.company = ref_doc.company
gateway_account = get_gateway_details(args) or frappe._dict()
grand_total = get_amount(ref_doc, gateway_account.get("payment_account"))
# Schedule-based PRs are allowed only if no Payment Entry exists for this document.
# Any existing Payment Entry forces legacy (amount-based) flow.
selected_payment_schedules = json.loads(args.get("schedules")) if args.get("schedules") else []
# Backend guard:
# If any Payment Entry exists, schedule-based PRs are not allowed.
if selected_payment_schedules and get_existing_payment_entry(ref_doc.name):
frappe.throw(
_(
"Payment Schedule based Payment Requests cannot be created because a Payment Entry already exists for this document."
)
)
has_payment_entry = bool(get_existing_payment_entry(ref_doc.name))
payment_reference = []
if selected_payment_schedules:
existing_payment_references = get_existing_payment_references(ref_doc.name)
if existing_payment_references:
existing_ids = {r["payment_schedule"] for r in existing_payment_references}
selected_ids = {r["name"] for r in selected_payment_schedules}
duplicate_ids = existing_ids & selected_ids
if duplicate_ids:
duplicate_schedules = []
for row in selected_payment_schedules:
if row["name"] in duplicate_ids:
existing_ref = next(
(r for r in existing_payment_references if r["payment_schedule"] == row["name"]),
{},
)
existing_pr = existing_ref.get("parent")
duplicate_schedules.append(
f"Payment Term: {row.get('payment_term')}, "
f"Due Date: {row.get('due_date')}, "
f"Amount: {row.get('payment_amount')} "
f"(already requested in PR {existing_pr})"
)
frappe.throw(
_("The following payment schedule(s) already exist:\n{0}").format(
"\n".join(duplicate_schedules)
)
)
payment_reference = set_payment_references(args.get("schedules"))
# Determine grand_total
if selected_payment_schedules and not has_payment_entry:
grand_total = sum(row.get("payment_amount") for row in selected_payment_schedules)
else:
grand_total = get_amount(ref_doc, gateway_account.get("payment_account"))
if not grand_total:
frappe.throw(_("Payment Entry is already created"))
@@ -561,7 +641,6 @@ def make_payment_request(**args):
loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points)) # sets fields on ref_doc
ref_doc.db_update()
grand_total = grand_total - loyalty_amount
# fetches existing payment request `grand_total` amount
existing_payment_request_amount = get_existing_payment_request_amount(ref_doc)
@@ -581,19 +660,20 @@ def make_payment_request(**args):
else:
# If PR's are processed, cancel all of them.
cancel_old_payment_requests(ref_doc.doctype, ref_doc.name)
else:
elif not selected_payment_schedules:
grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount)
draft_payment_request = frappe.db.get_value(
"Payment Request",
{"reference_doctype": ref_doc.doctype, "reference_name": ref_doc.name, "docstatus": 0},
)
if draft_payment_request:
frappe.db.set_value(
"Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False
)
pr = frappe.get_doc("Payment Request", draft_payment_request)
if selected_payment_schedules:
apply_payment_references(pr, payment_reference)
pr.save()
else:
bank_account = (
get_party_bank_account(args.get("party_type"), args.get("party"))
@@ -648,7 +728,10 @@ def make_payment_request(**args):
}
)
# Update dimensions
if selected_payment_schedules:
apply_payment_references(pr, payment_reference)
# Dimensions
pr.update(
{
"cost_center": ref_doc.get("cost_center"),
@@ -677,6 +760,51 @@ def make_payment_request(**args):
return pr.as_dict()
def apply_payment_references(pr, payment_reference):
existing_refs = pr.get("payment_reference") or []
existing_ids = {r.get("payment_schedule") for r in existing_refs if r.get("payment_schedule")}
new_refs = [r for r in (payment_reference or []) if r.get("payment_schedule") not in existing_ids]
pr.set("payment_reference", existing_refs + new_refs)
pr.set("grand_total", sum(flt(r.get("amount")) for r in pr.get("payment_reference")))
def set_payment_references(payment_schedules):
payment_schedules = json.loads(payment_schedules) if payment_schedules else []
payment_reference = []
for row in payment_schedules:
payment_reference.append(
{
"payment_term": row.get("payment_term"),
"payment_schedule": row.get("name"),
"description": row.get("description"),
"due_date": row.get("due_date"),
"amount": row.get("payment_amount"),
}
)
return payment_reference
def get_existing_payment_entry(ref_docname):
pe = frappe.qb.DocType("Payment Entry")
per = frappe.qb.DocType("Payment Entry Reference")
existing_pe = (
frappe.qb.from_(pe)
.join(per)
.on(per.parent == pe.name)
.select(pe.name)
.where(pe.docstatus < 2)
.where(per.reference_name == ref_docname)
.limit(1)
.run()
)
return existing_pe
def get_amount(ref_doc, payment_account=None):
"""get amount based on doctype"""
grand_total = 0
@@ -809,7 +937,7 @@ def get_payment_gateway_account(filter):
@frappe.whitelist()
def get_print_format_list(ref_doctype):
def get_print_format_list(ref_doctype: str):
print_format_list = ["Standard"]
print_format_list.extend(
@@ -819,13 +947,13 @@ def get_print_format_list(ref_doctype):
return {"print_format": print_format_list}
@frappe.whitelist(allow_guest=True)
def resend_payment_email(docname):
@frappe.whitelist()
def resend_payment_email(docname: str):
return frappe.get_doc("Payment Request", docname).send_email()
@frappe.whitelist()
def make_payment_entry(docname):
def make_payment_entry(docname: str):
doc = frappe.get_doc("Payment Request", docname)
return doc.create_payment_entry(submit=False).as_dict()
@@ -918,7 +1046,7 @@ def get_dummy_message(doc):
@frappe.whitelist()
def get_subscription_details(reference_doctype, reference_name):
def get_subscription_details(reference_doctype: str, reference_name: str):
if reference_doctype == "Sales Invoice":
subscriptions = frappe.db.sql(
"""SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice=%s""",
@@ -934,7 +1062,7 @@ def get_subscription_details(reference_doctype, reference_name):
@frappe.whitelist()
def make_payment_order(source_name, target_doc=None):
def make_payment_order(source_name: str, target_doc: str | Document | None = None):
from frappe.model.mapper import get_mapped_doc
def set_missing_values(source, target):
@@ -982,7 +1110,9 @@ def validate_payment(doc, method=None):
@frappe.whitelist()
def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len, filters):
def get_open_payment_requests_query(
doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict
):
# permission checks in `get_list()`
filters = frappe._dict(filters)
@@ -1021,3 +1151,44 @@ def get_irequests_of_payment_request(doc: str | None = None) -> list:
},
)
return res
@frappe.whitelist()
def get_available_payment_schedules(reference_doctype: str, reference_name: str):
ref_doc = frappe.get_doc(reference_doctype, reference_name)
if not hasattr(ref_doc, "payment_schedule") or not ref_doc.payment_schedule:
return []
if get_existing_payment_entry(reference_name):
return []
existing_refs = get_existing_payment_references(reference_name)
existing_ids = {r["payment_schedule"] for r in existing_refs if r.get("payment_schedule")}
return [r for r in ref_doc.payment_schedule if r.name not in existing_ids]
def get_existing_payment_references(reference_name):
PR = frappe.qb.DocType("Payment Request")
PRF = frappe.qb.DocType("Payment Reference")
result = (
frappe.qb.from_(PR)
.join(PRF)
.on(PR.name == PRF.parent)
.select(
PRF.payment_term,
PRF.due_date,
PRF.amount.as_("payment_amount"),
PRF.payment_schedule,
PRF.parent,
)
.where(PR.reference_name == reference_name)
.where(PR.docstatus < 2)
.where(
PR.status.isin(["Draft", "Requested", "Initiated", "Partially Paid", "Payment Ordered", "Paid"])
)
).run(as_dict=True)
return result

View File

@@ -1,11 +1,13 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import json
import re
from unittest.mock import patch
import frappe
from frappe.tests import IntegrationTestCase
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_terms_template
@@ -850,3 +852,130 @@ class TestPaymentRequest(IntegrationTestCase):
pr.load_from_db()
self.assertEqual(pr.grand_total, pi.outstanding_amount)
def test_payment_request_grand_total_from_selected_schedules(self):
po = create_purchase_order(do_not_save=1, currency="INR", qty=1, rate=100)
po.payment_schedule = []
po.append("payment_schedule", {"due_date": nowdate(), "payment_amount": 30})
po.append("payment_schedule", {"due_date": add_days(nowdate(), 1), "payment_amount": 30})
po.append("payment_schedule", {"due_date": add_days(nowdate(), 2), "payment_amount": 40})
po.save()
po.submit()
schedules = json.dumps(
[
{
"payment_term": row.payment_term,
"name": row.name,
"due_date": row.due_date,
"payment_amount": row.payment_amount,
"description": row.description,
}
for row in [po.payment_schedule[0], po.payment_schedule[2]]
]
)
pr = make_payment_request(
dt="Purchase Order",
dn=po.name,
mute_email=1,
submit_doc=False,
return_doc=True,
schedules=schedules,
)
pr.submit()
self.assertEqual(pr.grand_total, 70)
self.assertEqual(len(pr.payment_reference), 2)
def test_draft_pr_reuse_merges_payment_references(self):
from frappe.utils import add_days, nowdate
po = create_purchase_order(do_not_save=1, currency="INR", qty=1, rate=100)
po.payment_schedule = []
po.append("payment_schedule", {"due_date": nowdate(), "payment_amount": 50})
po.append("payment_schedule", {"due_date": add_days(nowdate(), 1), "payment_amount": 50})
po.save()
po.submit()
schedules = json.dumps(
[
{
"payment_term": row.payment_term,
"name": row.name,
"due_date": row.due_date,
"payment_amount": row.payment_amount,
"description": row.description,
}
for row in [po.payment_schedule[0]]
]
)
pr = make_payment_request(
dt="Purchase Order",
dn=po.name,
mute_email=1,
submit_doc=False,
return_doc=True,
schedules=schedules,
)
pr.save()
schedules = json.dumps(
[
{
"payment_term": row.payment_term,
"name": row.name,
"due_date": row.due_date,
"payment_amount": row.payment_amount,
"description": row.description,
}
for row in [po.payment_schedule[1]]
]
)
# call make_payment_request again → reuse draft
pr_reused = make_payment_request(
dt="Purchase Order",
dn=po.name,
mute_email=1,
submit_doc=False,
return_doc=True,
schedules=schedules,
)
self.assertEqual(pr.name, pr_reused.name)
self.assertEqual(pr_reused.grand_total, 100)
self.assertEqual(len(pr_reused.payment_reference), 2)
def test_schedule_pr_not_allowed_if_payment_entry_exists(self):
po = create_purchase_order(do_not_save=1, currency="INR", qty=1, rate=100)
po.payment_schedule = []
row = po.append("payment_schedule", {"due_date": nowdate(), "payment_amount": 100})
po.save()
po.submit()
# create PE first
pr = make_payment_request(dt="Purchase Order", dn=po.name, mute_email=1, submit_doc=1, return_doc=1)
pr.create_payment_entry()
schedules = json.dumps(
[
{
"name": row.name,
"payment_term": row.payment_term,
"due_date": row.due_date,
"payment_amount": row.payment_amount,
"description": row.description,
}
]
)
with self.assertRaises(frappe.ValidationError):
make_payment_request(
dt="Purchase Order",
dn=po.name,
mute_email=1,
submit_doc=False,
return_doc=True,
schedules=schedules,
)

View File

@@ -515,7 +515,7 @@ def delete_closing_entries(voucher_no):
@frappe.whitelist()
def get_period_start_end_date(fiscal_year, company):
def get_period_start_end_date(fiscal_year: str, company: str):
fy_start_date, fy_end_date = frappe.db.get_value(
"Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]
)

View File

@@ -4,19 +4,6 @@
frappe.ui.form.on("POS Closing Entry", {
onload: async function (frm) {
frm.ignore_doctypes_on_cancel_all = ["POS Invoice Merge Log", "Sales Invoice"];
frm.set_query("pos_profile", function (doc) {
return {
filters: { user: doc.user },
};
});
frm.set_query("user", function (doc) {
return {
query: "erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_cashiers",
filters: { parent: doc.pos_profile },
};
});
frm.set_query("pos_opening_entry", function (doc) {
return { filters: { status: "Open", docstatus: 1 } };
});

View File

@@ -2,6 +2,8 @@
# For license information, please see license.txt
from datetime import datetime
import frappe
from frappe import _
from frappe.query_builder import DocType
@@ -252,13 +254,13 @@ class POSClosingEntry(StatusUpdater):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_cashiers(doctype, txt, searchfield, start, page_len, filters):
def get_cashiers(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
cashiers_list = frappe.get_all("POS Profile User", filters=filters, fields=["user"], as_list=1)
return [c for c in cashiers_list]
@frappe.whitelist()
def get_invoices(start, end, pos_profile, user):
def get_invoices(start: str | datetime, end: str | datetime, pos_profile: str, user: str):
invoice_doctype = frappe.db.get_single_value("POS Settings", "invoice_type")
sales_inv_query = build_invoice_query("Sales Invoice", user, pos_profile, start, end)

View File

@@ -159,15 +159,16 @@
"language",
"column_break_84",
"select_print_heading",
"utm_analytics_section",
"utm_source",
"utm_medium",
"column_break_bhao",
"utm_campaign",
"more_information",
"inter_company_invoice_reference",
"customer_group",
"is_discounted",
"col_break23",
"utm_source",
"utm_campaign",
"utm_medium",
"column_break_gpiw",
"status",
"more_info",
"debit_to",
@@ -1541,10 +1542,6 @@
"fieldtype": "Check",
"label": "Update Billed Amount in Delivery Note"
},
{
"fieldname": "column_break_gpiw",
"fieldtype": "Column Break"
},
{
"fieldname": "utm_medium",
"fieldtype": "Link",
@@ -1610,13 +1607,24 @@
"hidden": 1,
"label": "Item Wise Tax Details",
"no_copy": 1,
"options": "Item Wise Tax Detail"
"options": "Item Wise Tax Detail",
"print_hide": 1
},
{
"collapsible": 1,
"fieldname": "utm_analytics_section",
"fieldtype": "Section Break",
"label": "UTM Analytics"
},
{
"fieldname": "column_break_bhao",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
"modified": "2025-08-04 22:22:31.471752",
"modified": "2026-02-10 14:23:07.181782",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",

View File

@@ -4,6 +4,7 @@
import frappe
from frappe import _, bold
from frappe.model.document import Document
from frappe.model.mapper import map_child_doc, map_doc
from frappe.query_builder.functions import IfNull, Sum
from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate
@@ -753,7 +754,7 @@ class POSInvoice(SalesInvoice):
return profile
@frappe.whitelist()
def set_missing_values(self, for_validate=False):
def set_missing_values(self, for_validate: bool = False):
profile = self.set_pos_fields(for_validate)
if not self.debit_to:
@@ -854,7 +855,7 @@ class POSInvoice(SalesInvoice):
return frappe.get_doc("Payment Request", pr)
@frappe.whitelist()
def update_payments(self, payments):
def update_payments(self, payments: list):
if self.status == "Consolidated":
frappe.throw(_("Create Payment Entry for Consolidated POS Invoices."))
@@ -897,7 +898,7 @@ class POSInvoice(SalesInvoice):
@frappe.whitelist()
def get_stock_availability(item_code, warehouse):
def get_stock_availability(item_code: str | None, warehouse: str):
if frappe.db.get_value("Item", item_code, "is_stock_item"):
is_stock_item = True
bin_qty = get_bin_qty(item_code, warehouse)
@@ -1020,14 +1021,14 @@ def get_pos_reserved_qty_from_table(child_table, item_code, warehouse):
@frappe.whitelist()
def make_sales_return(source_name, target_doc=None):
def make_sales_return(source_name: str, target_doc: Document | None = None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
return make_return_doc("POS Invoice", source_name, target_doc)
@frappe.whitelist()
def make_merge_log(invoices):
def make_merge_log(invoices: str | list):
import json
if isinstance(invoices, str):
@@ -1077,7 +1078,15 @@ def add_return_modes(doc, pos_profile):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
def item_query(
doctype: str,
txt: str,
searchfield: str,
start: int,
page_len: int,
filters: dict,
as_dict: bool = False,
):
if pos_profile := filters.get("pos_profile")[1]:
pos_profile = frappe.get_cached_doc("POS Profile", pos_profile)
if item_groups := get_item_group(pos_profile):

View File

@@ -897,6 +897,53 @@ class TestPOSInvoice(IntegrationTestCase):
if batch.batch_no == batch_no and batch.warehouse == "_Test Warehouse - _TC":
self.assertEqual(batch.qty, 5)
def test_pos_batch_reservation_with_return_qty(self):
"""
Test POS Invoice reserved qty for batch without bundle with return invoices.
"""
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
get_auto_batch_nos,
)
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_batch_item_with_batch,
)
create_batch_item_with_batch("_Batch Item Reserve Return", "TestBatch-RR 01")
se = make_stock_entry(
target="_Test Warehouse - _TC",
item_code="_Batch Item Reserve Return",
qty=30,
basic_rate=100,
)
se.reload()
batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
# POS Invoice for the batch without bundle
pos_inv = create_pos_invoice(item="_Batch Item Reserve Return", rate=300, qty=15, do_not_save=1)
pos_inv.append(
"payments",
{"mode_of_payment": "Cash", "amount": 4500},
)
pos_inv.items[0].batch_no = batch_no
pos_inv.save()
pos_inv.submit()
# POS Invoice return
pos_return = make_sales_return(pos_inv.name)
pos_return.insert()
pos_return.submit()
batches = get_auto_batch_nos(
frappe._dict({"item_code": "_Batch Item Reserve Return", "warehouse": "_Test Warehouse - _TC"})
)
for batch in batches:
if batch.batch_no == batch_no and batch.warehouse == "_Test Warehouse - _TC":
self.assertEqual(batch.qty, 30)
def test_pos_batch_item_qty_validation(self):
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
BatchNegativeStockError,

View File

@@ -12,56 +12,78 @@
"disabled",
"column_break_9",
"warehouse",
"utm_source",
"utm_campaign",
"utm_medium",
"company_address",
"section_break_15",
"applicable_for_users",
"accounting_tab",
"section_break_11",
"payments",
"set_grand_total_to_default_mop",
"price_list_and_currency_section",
"currency",
"column_break_bptt",
"selling_price_list",
"write_off_section",
"write_off_account",
"column_break_ukpz",
"write_off_cost_center",
"column_break_pkca",
"write_off_limit",
"income_and_expense_account",
"income_account",
"column_break_byzk",
"expense_account",
"taxes_section",
"taxes_and_charges",
"column_break_cjpp",
"tax_category",
"section_break_19",
"account_for_change_amount",
"disable_rounded_total",
"column_break_23",
"apply_discount_on",
"allow_partial_payment",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"pos_configurations_tab",
"section_break_14",
"hide_images",
"hide_unavailable_items",
"auto_add_item_to_cart",
"validate_stock_on_save",
"print_receipt_on_order_complete",
"action_on_new_invoice",
"validate_stock_on_save",
"column_break_16",
"update_stock",
"ignore_pricing_rule",
"print_receipt_on_order_complete",
"pos_item_selector_section",
"hide_images",
"column_break_rpny",
"hide_unavailable_items",
"column_break_stcl",
"auto_add_item_to_cart",
"pos_item_details_section",
"allow_rate_change",
"column_break_hwfg",
"allow_discount_change",
"set_grand_total_to_default_mop",
"allow_partial_payment",
"column_break_egpi",
"allow_warehouse_change",
"section_break_15",
"applicable_for_users",
"section_break_23",
"item_groups",
"column_break_25",
"customer_groups",
"more_info_tab",
"section_break_16",
"print_format",
"letter_head",
"column_break0",
"tc_name",
"select_print_heading",
"section_break_19",
"selling_price_list",
"currency",
"write_off_account",
"write_off_cost_center",
"write_off_limit",
"account_for_change_amount",
"disable_rounded_total",
"column_break_23",
"income_account",
"expense_account",
"taxes_and_charges",
"tax_category",
"apply_discount_on",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project"
"utm_analytics_section",
"utm_source",
"column_break_tvls",
"utm_campaign",
"column_break_xygw",
"utm_medium"
],
"fields": [
{
@@ -130,8 +152,7 @@
},
{
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"label": "Configuration"
"fieldtype": "Section Break"
},
{
"description": "Only show Items from these Item Groups",
@@ -152,6 +173,7 @@
"options": "POS Customer Group"
},
{
"collapsible": 1,
"fieldname": "section_break_16",
"fieldtype": "Section Break",
"label": "Print Settings"
@@ -191,7 +213,7 @@
{
"fieldname": "section_break_19",
"fieldtype": "Section Break",
"label": "Accounting"
"label": "Miscellaneous"
},
{
"fieldname": "selling_price_list",
@@ -427,9 +449,111 @@
},
{
"default": "0",
"description": "Applicable only on Transactions made using POS",
"fieldname": "allow_partial_payment",
"fieldtype": "Check",
"label": "Allow Partial Payment"
},
{
"fieldname": "column_break_tvls",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_xygw",
"fieldtype": "Column Break"
},
{
"fieldname": "accounting_tab",
"fieldtype": "Tab Break",
"label": "Accounting"
},
{
"fieldname": "more_info_tab",
"fieldtype": "Tab Break",
"label": "More Info"
},
{
"fieldname": "pos_configurations_tab",
"fieldtype": "Tab Break",
"label": "POS Configurations"
},
{
"fieldname": "price_list_and_currency_section",
"fieldtype": "Section Break",
"label": "Price List & Currency"
},
{
"fieldname": "column_break_bptt",
"fieldtype": "Column Break"
},
{
"fieldname": "write_off_section",
"fieldtype": "Section Break",
"label": "Write Off"
},
{
"fieldname": "column_break_ukpz",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_pkca",
"fieldtype": "Column Break"
},
{
"fieldname": "income_and_expense_account",
"fieldtype": "Section Break",
"label": "Income and Expense"
},
{
"fieldname": "column_break_byzk",
"fieldtype": "Column Break"
},
{
"fieldname": "taxes_section",
"fieldtype": "Section Break",
"label": "Taxes"
},
{
"fieldname": "column_break_cjpp",
"fieldtype": "Column Break"
},
{
"fieldname": "pos_item_selector_section",
"fieldtype": "Section Break",
"label": "POS Item Selector"
},
{
"fieldname": "column_break_rpny",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_stcl",
"fieldtype": "Column Break"
},
{
"fieldname": "pos_item_details_section",
"fieldtype": "Section Break",
"label": "POS Item Details"
},
{
"fieldname": "column_break_hwfg",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_egpi",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "allow_warehouse_change",
"fieldtype": "Check",
"label": "Allow User to Edit Warehouse"
},
{
"collapsible": 1,
"fieldname": "utm_analytics_section",
"fieldtype": "Section Break",
"label": "Campaign"
}
],
"grid_page_length": 50,
@@ -458,7 +582,7 @@
"link_fieldname": "pos_profile"
}
],
"modified": "2025-06-24 11:19:19.834905",
"modified": "2026-02-10 14:24:48.597412",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",

View File

@@ -34,6 +34,7 @@ class POSProfile(Document):
allow_discount_change: DF.Check
allow_partial_payment: DF.Check
allow_rate_change: DF.Check
allow_warehouse_change: DF.Check
applicable_for_users: DF.Table[POSProfileUser]
apply_discount_on: DF.Literal["Grand Total", "Net Total"]
auto_add_item_to_cart: DF.Check
@@ -274,7 +275,7 @@ def get_child_nodes(group_type, root):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
def pos_profile_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
user = frappe.session["user"]
company = filters.get("company") or frappe.defaults.get_user_default("company")
@@ -318,7 +319,7 @@ def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
def set_default_profile(pos_profile, company):
def set_default_profile(pos_profile: str, company: str):
modified = now()
user = frappe.session.user

View File

@@ -98,8 +98,7 @@ def get_customers_list(pos_profile=None):
return (
frappe.db.sql(
f""" select name, customer_name, customer_group,
territory, customer_pos_id from tabCustomer where disabled = 0
f""" select name, customer_name, customer_group, territory from tabCustomer where disabled = 0
and {cond}""",
tuple(customer_groups),
as_dict=1,

View File

@@ -121,7 +121,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Apply On",
"options": "\nItem Code\nItem Group\nBrand\nTransaction",
"options": "Item Code\nItem Group\nBrand\nTransaction",
"reqd": 1
},
{
@@ -657,7 +657,7 @@
"icon": "fa fa-gift",
"idx": 1,
"links": [],
"modified": "2025-08-20 11:40:07.096854",
"modified": "2026-02-17 12:24:07.553505",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",
@@ -714,9 +714,10 @@
"write": 1
}
],
"row_format": "Dynamic",
"show_name_in_global_search": 1,
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"title_field": "title"
}
}

View File

@@ -45,7 +45,7 @@ class PricingRule(Document):
apply_discount_on: DF.Literal["Grand Total", "Net Total"]
apply_discount_on_rate: DF.Check
apply_multiple_pricing_rules: DF.Check
apply_on: DF.Literal["", "Item Code", "Item Group", "Brand", "Transaction"]
apply_on: DF.Literal["Item Code", "Item Group", "Brand", "Transaction"]
apply_recursion_over: DF.Float
apply_rule_on_other: DF.Literal["", "Item Code", "Item Group", "Brand"]
brands: DF.Table[PricingRuleBrand]
@@ -320,7 +320,7 @@ class PricingRule(Document):
@frappe.whitelist()
def apply_pricing_rule(args, doc=None):
def apply_pricing_rule(args: str | dict, doc: str | dict | Document | None = None):
"""
args = {
"items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
@@ -346,8 +346,7 @@ def apply_pricing_rule(args, doc=None):
args = frappe._dict(args)
if not args.transaction_type:
set_transaction_type(args)
set_transaction_type(args)
# list of dictionaries
out = []
@@ -618,7 +617,12 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
@frappe.whitelist()
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None, rate=None):
def remove_pricing_rule_for_item(
pricing_rules: str | None,
item_details: str | frappe._dict,
item_code: str | None = None,
rate: float | None = None,
):
from erpnext.accounts.doctype.pricing_rule.utils import (
get_applied_pricing_rules,
get_pricing_rule_items,
@@ -666,7 +670,7 @@ def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None, ra
@frappe.whitelist()
def remove_pricing_rules(item_list):
def remove_pricing_rules(item_list: str | list):
if isinstance(item_list, str):
item_list = json.loads(item_list)
@@ -683,28 +687,28 @@ def remove_pricing_rules(item_list):
return out
def set_transaction_type(args):
if args.transaction_type:
def set_transaction_type(pricing_ctx: frappe._dict) -> None:
if pricing_ctx.transaction_type in ["buying", "selling"]:
return
if args.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
args.transaction_type = "selling"
elif args.doctype in (
if pricing_ctx.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
pricing_ctx.transaction_type = "selling"
elif pricing_ctx.doctype in (
"Material Request",
"Supplier Quotation",
"Purchase Order",
"Purchase Receipt",
"Purchase Invoice",
):
args.transaction_type = "buying"
elif args.customer:
args.transaction_type = "selling"
pricing_ctx.transaction_type = "buying"
elif pricing_ctx.customer:
pricing_ctx.transaction_type = "selling"
else:
args.transaction_type = "buying"
pricing_ctx.transaction_type = "buying"
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
def get_item_uoms(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
items = [filters.get("value")]
if filters.get("apply_on") != "Item Code":
field = frappe.scrub(filters.get("apply_on"))

View File

@@ -10,7 +10,7 @@
],
"fields": [
{
"depends_on": "eval:parent.apply_on == 'Item Code'",
"depends_on": "eval:parent.apply_on == 'Brand'",
"fieldname": "brand",
"fieldtype": "Link",
"in_list_view": 1,
@@ -28,14 +28,15 @@
],
"istable": 1,
"links": [],
"modified": "2024-03-27 13:10:17.857046",
"modified": "2026-02-17 12:17:13.073587",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule Brand",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -10,7 +10,7 @@
],
"fields": [
{
"depends_on": "eval:parent.apply_on == 'Item Code'",
"depends_on": "eval:parent.apply_on == 'Item Group'",
"fieldname": "item_group",
"fieldtype": "Link",
"in_list_view": 1,
@@ -28,14 +28,15 @@
],
"istable": 1,
"links": [],
"modified": "2024-03-27 13:10:18.221095",
"modified": "2026-02-17 12:16:57.778471",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule Item Group",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -415,8 +415,9 @@ def reconcile(doc: None | str = None) -> None:
for x in allocations:
pr.append("allocation", x)
skip_ref_details_update_for_pe = check_multi_currency(pr)
# reconcile
pr.reconcile_allocations(skip_ref_details_update_for_pe=True)
pr.reconcile_allocations(skip_ref_details_update_for_pe=skip_ref_details_update_for_pe)
# If Payment Entry, update details only for newly linked references
# This is for performance
@@ -504,6 +505,37 @@ def reconcile(doc: None | str = None) -> None:
frappe.db.set_value("Process Payment Reconciliation", doc, "status", "Completed")
def check_multi_currency(pr_doc):
GL = frappe.qb.DocType("GL Entry")
Account = frappe.qb.DocType("Account")
def get_account_currency(voucher_type, voucher_no):
currency = (
frappe.qb.from_(GL)
.join(Account)
.on(GL.account == Account.name)
.select(Account.account_currency)
.where(
(GL.voucher_type == voucher_type)
& (GL.voucher_no == voucher_no)
& (Account.account_type.isin(["Payable", "Receivable"]))
)
.limit(1)
).run(as_dict=True)
return currency[0].account_currency if currency else None
for allocation in pr_doc.allocation:
reference_currency = get_account_currency(allocation.reference_type, allocation.reference_name)
invoice_currency = get_account_currency(allocation.invoice_type, allocation.invoice_number)
if reference_currency != invoice_currency:
return True
return False
@frappe.whitelist()
def is_any_doc_running(for_filter: str | dict | None = None) -> str | None:
running_doc = None

View File

@@ -420,7 +420,7 @@ def get_context(customer, doc):
@frappe.whitelist()
def fetch_customers(customer_collection, collection_name, primary_mandatory):
def fetch_customers(customer_collection: str, collection_name: str, primary_mandatory: str | int):
customer_list = []
customers = []
@@ -460,7 +460,7 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory):
@frappe.whitelist()
def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=True):
def get_customer_emails(customer_name: str, primary_mandatory: str | int, billing_and_primary: bool = True):
"""Returns first email from Contact Email table as a Billing email
when Is Billing Contact checked
and Primary email- email with Is Primary checked"""
@@ -506,7 +506,7 @@ def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=Tr
@frappe.whitelist()
def download_statements(document_name):
def download_statements(document_name: str):
doc = frappe.get_doc("Process Statement Of Accounts", document_name)
report = get_report_pdf(doc)
if report:
@@ -516,7 +516,7 @@ def download_statements(document_name):
@frappe.whitelist()
def send_emails(document_name, from_scheduler=False, posting_date=None):
def send_emails(document_name: str, from_scheduler: bool = False, posting_date: str | None = None):
doc = frappe.get_doc("Process Statement Of Accounts", document_name)
report = get_report_pdf(doc, consolidated=False)

View File

@@ -18,8 +18,19 @@ class TestProcessStatementOfAccounts(AccountsTestMixin, IntegrationTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
letterhead = frappe.get_doc("Letter Head", "Company Letterhead - Grey")
letterhead.is_default = 0
letterhead.save()
cls.enterClassContext(cls.change_settings("Selling Settings", validate_selling_price=0))
@classmethod
def tearDownClass(cls):
super().tearDownClass()
letterhead = frappe.get_doc("Letter Head", "Company Letterhead - Grey")
letterhead.is_default = 1
letterhead.save()
frappe.db.commit() # nosemgrep
def setUp(self):
self.create_company()
self.create_customer()

View File

@@ -134,7 +134,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
this.frm.add_custom_button(
__("Payment Request"),
function () {
me.make_payment_request();
me.make_payment_request_with_schedule();
},
__("Create")
);
@@ -534,7 +534,7 @@ cur_frm.fields_dict["select_print_heading"].get_query = function (doc, cdt, cdn)
cur_frm.set_query("wip_composite_asset", "items", function () {
return {
filters: { is_composite_asset: 1, docstatus: 0 },
filters: { asset_type: "Composite Asset", docstatus: 0 },
};
});

View File

@@ -85,20 +85,24 @@
"taxes_and_charges_added",
"taxes_and_charges_deducted",
"total_taxes_and_charges",
"section_break_49",
"totals_section",
"use_company_roundoff_cost_center",
"grand_total",
"in_words",
"column_break8",
"disable_rounded_total",
"rounding_adjustment",
"rounded_total",
"base_totals_section",
"base_grand_total",
"base_in_words",
"column_break_hcca",
"base_rounding_adjustment",
"base_rounded_total",
"base_in_words",
"column_break8",
"grand_total",
"rounding_adjustment",
"use_company_roundoff_cost_center",
"rounded_total",
"in_words",
"section_break_ttrv",
"total_advance",
"column_break_peap",
"outstanding_amount",
"disable_rounded_total",
"section_tax_withholding_entry",
"tax_withholding_group",
"ignore_tax_withholding_threshold",
@@ -606,6 +610,7 @@
},
{
"default": "0",
"depends_on": "eval:doc.items.every((item) => !item.pr_detail)",
"fieldname": "update_stock",
"fieldtype": "Check",
"label": "Update Stock",
@@ -882,15 +887,10 @@
"options": "currency",
"print_hide": 1
},
{
"fieldname": "section_break_49",
"fieldtype": "Section Break",
"label": "Totals"
},
{
"fieldname": "base_grand_total",
"fieldtype": "Currency",
"label": "Grand Total (Company Currency)",
"label": "Grand Total",
"oldfieldname": "grand_total",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
@@ -901,7 +901,7 @@
"depends_on": "eval:!doc.disable_rounded_total",
"fieldname": "base_rounding_adjustment",
"fieldtype": "Currency",
"label": "Rounding Adjustment (Company Currency)",
"label": "Rounding Adjustment",
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
@@ -911,7 +911,7 @@
"depends_on": "eval:!doc.disable_rounded_total",
"fieldname": "base_rounded_total",
"fieldtype": "Currency",
"label": "Rounded Total (Company Currency)",
"label": "Rounded Total",
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
@@ -920,7 +920,7 @@
{
"fieldname": "base_in_words",
"fieldtype": "Data",
"label": "In Words (Company Currency)",
"label": "In Words",
"length": 240,
"oldfieldname": "in_words",
"oldfieldtype": "Data",
@@ -1660,6 +1660,28 @@
"fieldname": "override_tax_withholding_entries",
"fieldtype": "Check",
"label": "Edit Tax Withholding Entries"
},
{
"fieldname": "column_break_hcca",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_ttrv",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_peap",
"fieldtype": "Column Break"
},
{
"fieldname": "base_totals_section",
"fieldtype": "Section Break",
"label": "Totals (Company Currency)"
},
{
"fieldname": "totals_section",
"fieldtype": "Section Break",
"label": "Totals"
}
],
"grid_page_length": 50,
@@ -1667,7 +1689,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2025-12-15 06:41:38.237728",
"modified": "2026-02-23 13:23:57.269770",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -6,6 +6,7 @@ import json
import frappe
from frappe import _, qb, throw
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from frappe.query_builder.functions import Sum
from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
@@ -36,7 +37,7 @@ from erpnext.accounts.utils import get_account_currency, get_fiscal_year, update
from erpnext.assets.doctype.asset.asset import is_cwip_accounting_enabled
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.controllers.accounts_controller import validate_account_head
from erpnext.controllers.accounts_controller import merge_taxes, validate_account_head
from erpnext.controllers.buying_controller import BuyingController
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
update_billed_amount_based_on_po,
@@ -1745,10 +1746,6 @@ class PurchaseInvoice(BuyingController):
project_doc.db_update()
def validate_supplier_invoice(self):
if self.bill_date:
if getdate(self.bill_date) > getdate(self.posting_date):
frappe.throw(_("Supplier Invoice Date cannot be greater than Posting Date"))
if self.bill_no:
if cint(frappe.get_single_value("Accounts Settings", "check_supplier_invoice_uniqueness")):
fiscal_year = get_fiscal_year(self.posting_date, company=self.company, as_dict=True)
@@ -1945,14 +1942,14 @@ def make_regional_gl_entries(gl_entries, doc):
@frappe.whitelist()
def make_debit_note(source_name, target_doc=None):
def make_debit_note(source_name: str, target_doc: str | Document | None = None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
return make_return_doc("Purchase Invoice", source_name, target_doc)
@frappe.whitelist()
def make_stock_entry(source_name, target_doc=None):
def make_stock_entry(source_name: str, target_doc: str | Document | None = None):
doc = get_mapped_doc(
"Purchase Invoice",
source_name,
@@ -1970,44 +1967,54 @@ def make_stock_entry(source_name, target_doc=None):
@frappe.whitelist()
def change_release_date(name, release_date=None):
def change_release_date(name: str, release_date: str | None = None):
if frappe.db.exists("Purchase Invoice", name):
pi = frappe.get_lazy_doc("Purchase Invoice", name)
pi.db_set("release_date", release_date)
@frappe.whitelist()
def unblock_invoice(name):
def unblock_invoice(name: str):
if frappe.db.exists("Purchase Invoice", name):
pi = frappe.get_lazy_doc("Purchase Invoice", name)
pi.unblock_invoice()
@frappe.whitelist()
def block_invoice(name, release_date, hold_comment=None):
def block_invoice(name: str, release_date: str, hold_comment: str | None = None):
if frappe.db.exists("Purchase Invoice", name):
pi = frappe.get_lazy_doc("Purchase Invoice", name)
pi.block_invoice(hold_comment, release_date)
@frappe.whitelist()
def make_inter_company_sales_invoice(source_name, target_doc=None):
def make_inter_company_sales_invoice(source_name: str, target_doc: Document | None = None):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
@frappe.whitelist()
def make_purchase_receipt(source_name, target_doc=None, args=None):
def make_purchase_receipt(
source_name: str, target_doc: str | Document | None = None, args: str | dict | None = None
):
if args is None:
args = {}
if isinstance(args, str):
args = json.loads(args)
def post_parent_process(source_parent, target_parent):
for row in target_parent.get("items"):
if row.get("qty") == 0:
target_parent.remove(row)
remove_items_with_zero_qty(target_parent)
set_missing_values(source_parent, target_parent)
def remove_items_with_zero_qty(target_parent):
target_parent.items = [row for row in target_parent.get("items") if row.get("qty") != 0]
def set_missing_values(source_parent, target_parent):
target_parent.run_method("set_missing_values")
if args and args.get("merge_taxes"):
merge_taxes(source_parent, target_parent)
target_parent.run_method("calculate_taxes_and_totals")
def update_item(obj, target, source_parent):
from erpnext.controllers.sales_and_purchase_return import get_returned_qty_map_for_row
@@ -2059,7 +2066,11 @@ def make_purchase_receipt(source_name, target_doc=None, args=None):
"postprocess": update_item,
"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) and select_item(doc),
},
"Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges"},
"Purchase Taxes and Charges": {
"doctype": "Purchase Taxes and Charges",
"reset_value": not (args and args.get("merge_taxes")),
"ignore": args.get("merge_taxes") if args else 0,
},
},
target_doc,
post_parent_process,

View File

@@ -52,6 +52,7 @@
"stock_uom_rate",
"is_free_item",
"apply_tds",
"allow_zero_valuation_rate",
"section_break_22",
"net_rate",
"net_amount",
@@ -97,7 +98,6 @@
"service_start_date",
"service_end_date",
"reference",
"allow_zero_valuation_rate",
"item_tax_rate",
"bom",
"include_exploded_items",
@@ -420,6 +420,7 @@
"options": "UOM"
},
{
"depends_on": "eval:parent.update_stock",
"fieldname": "warehouse_section",
"fieldtype": "Section Break",
"label": "Warehouse"
@@ -447,7 +448,6 @@
"print_hide": 1
},
{
"depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
"fieldname": "batch_no",
"fieldtype": "Link",
"label": "Batch No",
@@ -459,14 +459,12 @@
"fieldtype": "Column Break"
},
{
"depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
"fieldname": "serial_no",
"fieldtype": "Text",
"label": "Serial No",
"no_copy": 1
},
{
"depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
"fieldname": "rejected_serial_no",
"fieldtype": "Text",
"label": "Rejected Serial No",
@@ -577,6 +575,7 @@
},
{
"default": "0",
"depends_on": "eval:parent.update_stock",
"fieldname": "allow_zero_valuation_rate",
"fieldtype": "Check",
"label": "Allow Zero Valuation Rate",
@@ -800,7 +799,7 @@
"read_only": 1
},
{
"depends_on": "eval:parent.is_internal_supplier && parent.update_stock",
"depends_on": "eval:parent.is_internal_supplier",
"fieldname": "from_warehouse",
"fieldtype": "Link",
"ignore_user_permissions": 1,
@@ -896,7 +895,7 @@
"label": "Consider for Tax Withholding"
},
{
"depends_on": "eval:parent.update_stock == 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)",
"depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
"fieldname": "serial_and_batch_bundle",
"fieldtype": "Link",
"label": "Serial and Batch Bundle",
@@ -906,7 +905,7 @@
"search_index": 1
},
{
"depends_on": "eval:parent.update_stock == 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)",
"depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
"fieldname": "rejected_serial_and_batch_bundle",
"fieldtype": "Link",
"label": "Rejected Serial and Batch Bundle",
@@ -922,7 +921,7 @@
"options": "Asset"
},
{
"depends_on": "eval:parent.update_stock === 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)",
"depends_on": "eval:doc.use_serial_batch_fields === 0 && doc.docstatus === 0",
"fieldname": "add_serial_batch_bundle",
"fieldtype": "Button",
"label": "Add Serial / Batch No"
@@ -992,7 +991,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2025-12-13 14:10:02.379392",
"modified": "2026-02-15 21:07:49.455930",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@@ -152,7 +152,7 @@ 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
@@ -286,7 +286,9 @@ def validate_docs_for_voucher_types(doc_voucher_types):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters):
def get_repost_allowed_types(
doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict
):
filters = {"allowed": True}
if txt:

View File

@@ -21,7 +21,7 @@ def repost_ple_for_voucher(voucher_type, voucher_no, gle_map=None):
@frappe.whitelist()
def start_payment_ledger_repost(docname=None):
def start_payment_ledger_repost(docname: str | None = None):
"""
Repost Payment Ledger Entries for Vouchers through Background Job
"""
@@ -119,7 +119,7 @@ class RepostPaymentLedger(Document):
@frappe.whitelist()
def execute_repost_payment_ledger(docname):
def execute_repost_payment_ledger(docname: str):
"""Repost Payment Ledger Entries by background job."""
job_name = "payment_ledger_repost_" + docname

View File

@@ -44,6 +44,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
"Unreconcile Payment Entries",
"Serial and Batch Bundle",
"Bank Transaction",
"Packing Slip",
];
if (!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
@@ -137,7 +138,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
this.frm.add_custom_button(
__("Payment Request"),
function () {
me.make_payment_request();
me.make_payment_request_with_schedule();
},
__("Create")
);

View File

@@ -7,12 +7,12 @@
"engine": "InnoDB",
"field_order": [
"customer_section",
"company",
"company_tax_id",
"naming_series",
"customer",
"customer_name",
"tax_id",
"company",
"company_tax_id",
"column_break1",
"posting_date",
"posting_time",
@@ -77,34 +77,38 @@
"base_total_taxes_and_charges",
"column_break_47",
"total_taxes_and_charges",
"totals",
"totals_section",
"use_company_roundoff_cost_center",
"grand_total",
"in_words",
"column_break5",
"disable_rounded_total",
"rounding_adjustment",
"rounded_total",
"base_totals_section",
"base_grand_total",
"base_in_words",
"column_break_xjag",
"base_rounding_adjustment",
"base_rounded_total",
"base_in_words",
"column_break5",
"grand_total",
"rounding_adjustment",
"use_company_roundoff_cost_center",
"rounded_total",
"in_words",
"section_break_vacb",
"total_advance",
"column_break_rdks",
"outstanding_amount",
"disable_rounded_total",
"section_tax_withholding_entry",
"tax_withholding_group",
"ignore_tax_withholding_threshold",
"override_tax_withholding_entries",
"tax_withholding_entries",
"section_break_49",
"additional_discount_section",
"apply_discount_on",
"base_discount_amount",
"coupon_code",
"is_cash_or_non_trade_discount",
"additional_discount_account",
"column_break_51",
"additional_discount_percentage",
"discount_amount",
"is_cash_or_non_trade_discount",
"additional_discount_account",
"sec_tax_breakup",
"other_charges_calculation",
"item_wise_tax_details",
@@ -194,13 +198,13 @@
"column_break8",
"unrealized_profit_loss_account",
"against_income_account",
"sales_team_section_break",
"commission_section",
"sales_partner",
"amount_eligible_for_commission",
"column_break10",
"commission_rate",
"total_commission",
"section_break2",
"sales_team_section",
"sales_team",
"edit_printing_settings",
"letter_head",
@@ -215,20 +219,21 @@
"column_break_140",
"to_date",
"update_auto_repeat_reference",
"utm_analytics_section",
"utm_source",
"utm_medium",
"column_break_ixxw",
"utm_campaign",
"utm_content",
"more_information",
"status",
"inter_company_invoice_reference",
"represents_company",
"remarks",
"customer_group",
"column_break_imbx",
"utm_source",
"utm_campaign",
"utm_medium",
"utm_content",
"col_break23",
"is_internal_customer",
"represents_company",
"inter_company_invoice_reference",
"is_discounted",
"remarks",
"connections_tab"
],
"fields": [
@@ -266,6 +271,7 @@
"oldfieldtype": "Link",
"options": "Customer",
"print_hide": 1,
"reqd": 1,
"search_index": 1
},
{
@@ -703,6 +709,7 @@
},
{
"default": "0",
"depends_on": "eval:doc.items.every((item) => !item.dn_detail)",
"fieldname": "update_stock",
"fieldtype": "Check",
"hide_days": 1,
@@ -1072,14 +1079,6 @@
"no_copy": 1,
"options": "Cost Center"
},
{
"collapsible": 1,
"fieldname": "section_break_49",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Additional Discount"
},
{
"default": "Grand Total",
"fieldname": "apply_discount_on",
@@ -1124,22 +1123,12 @@
"options": "currency",
"print_hide": 1
},
{
"fieldname": "totals",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Totals",
"oldfieldtype": "Section Break",
"options": "fa fa-money",
"print_hide": 1
},
{
"fieldname": "base_grand_total",
"fieldtype": "Currency",
"hide_days": 1,
"hide_seconds": 1,
"label": "Grand Total (Company Currency)",
"label": "Grand Total",
"oldfieldname": "grand_total",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
@@ -1153,9 +1142,8 @@
"fieldtype": "Currency",
"hide_days": 1,
"hide_seconds": 1,
"label": "Rounding Adjustment (Company Currency)",
"label": "Rounding Adjustment",
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1
},
@@ -1165,10 +1153,9 @@
"fieldtype": "Currency",
"hide_days": 1,
"hide_seconds": 1,
"label": "Rounded Total (Company Currency)",
"label": "Rounded Total",
"oldfieldname": "rounded_total",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1
},
@@ -1178,7 +1165,7 @@
"fieldtype": "Small Text",
"hide_days": 1,
"hide_seconds": 1,
"label": "In Words (Company Currency)",
"label": "In Words",
"length": 240,
"oldfieldname": "in_words",
"oldfieldtype": "Data",
@@ -1271,7 +1258,6 @@
"read_only": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "advances",
"fieldname": "advances_section",
"fieldtype": "Section Break",
@@ -1647,13 +1633,6 @@
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "col_break23",
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1,
"width": "50%"
},
{
"default": "Draft",
"fieldname": "status",
@@ -1705,10 +1684,10 @@
"read_only": 1
},
{
"allow_on_submit": 1,
"default": "No",
"fieldname": "is_opening",
"fieldtype": "Select",
"hidden": 1,
"hide_days": 1,
"hide_seconds": 1,
"label": "Is Opening Entry",
@@ -1737,18 +1716,6 @@
"oldfieldtype": "Text",
"print_hide": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "sales_partner",
"fieldname": "sales_team_section_break",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Commission",
"oldfieldtype": "Section Break",
"options": "fa fa-group",
"print_hide": 1
},
{
"fieldname": "sales_partner",
"fieldtype": "Link",
@@ -1792,16 +1759,6 @@
"options": "Company:company:default_currency",
"print_hide": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "sales_team",
"fieldname": "section_break2",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Sales Team",
"print_hide": 1
},
{
"allow_on_submit": 1,
"fieldname": "sales_team",
@@ -2250,7 +2207,8 @@
"hidden": 1,
"label": "Item Wise Tax Details",
"no_copy": 1,
"options": "Item Wise Tax Detail"
"options": "Item Wise Tax Detail",
"print_hide": 1
},
{
"default": "0",
@@ -2291,6 +2249,74 @@
"fieldname": "override_tax_withholding_entries",
"fieldtype": "Check",
"label": "Edit Tax Withholding Entries"
},
{
"fieldname": "totals_section",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Totals",
"oldfieldtype": "Section Break",
"options": "fa fa-money",
"print_hide": 1
},
{
"fieldname": "base_totals_section",
"fieldtype": "Section Break",
"label": "Totals (Company Currency)",
"options": "Company:company:default_currency"
},
{
"fieldname": "column_break_xjag",
"fieldtype": "Column Break"
},
{
"collapsible": 1,
"fieldname": "additional_discount_section",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Additional Discount"
},
{
"collapsible": 1,
"collapsible_depends_on": "sales_team",
"fieldname": "sales_team_section",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Sales Team",
"print_hide": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "sales_partner",
"fieldname": "commission_section",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Commission",
"oldfieldtype": "Section Break",
"options": "fa fa-group",
"print_hide": 1
},
{
"fieldname": "column_break_ixxw",
"fieldtype": "Column Break"
},
{
"collapsible": 1,
"fieldname": "utm_analytics_section",
"fieldtype": "Section Break",
"label": "UTM Analytics"
},
{
"fieldname": "section_break_vacb",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_rdks",
"fieldtype": "Column Break"
}
],
"grid_page_length": 50,
@@ -2304,7 +2330,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2025-12-24 18:29:50.242618",
"modified": "2026-02-28 17:58:56.453076",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -6,6 +6,7 @@ import frappe
import frappe.utils
from frappe import _, msgprint, throw
from frappe.contacts.doctype.address.address import get_address_display
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from frappe.model.utils import get_fetch_values
from frappe.query_builder import Case
@@ -119,7 +120,7 @@ class SalesInvoice(SellingController):
cost_center: DF.Link | None
coupon_code: DF.Link | None
currency: DF.Link
customer: DF.Link | None
customer: DF.Link
customer_address: DF.Link | None
customer_group: DF.Link | None
customer_name: DF.SmallText | None
@@ -741,7 +742,7 @@ class SalesInvoice(SellingController):
pos_invoice_doc.cancel()
@frappe.whitelist()
def set_missing_values(self, for_validate=False):
def set_missing_values(self, for_validate: bool = False):
pos = self.set_pos_fields(for_validate)
if not self.debit_to:
@@ -1451,6 +1452,9 @@ class SalesInvoice(SellingController):
return asset_qty_map
def process_asset_depreciation(self):
if self.is_internal_transfer():
return
if (self.is_return and self.docstatus == 2) or (not self.is_return and self.docstatus == 1):
self.depreciate_asset_on_sale()
else:
@@ -2409,7 +2413,7 @@ def get_list_context(context=None):
@frappe.whitelist()
def get_bank_cash_account(mode_of_payment, company):
def get_bank_cash_account(mode_of_payment: str, company: str):
account = frappe.db.get_value(
"Mode of Payment Account", {"parent": mode_of_payment, "company": company}, "default_account"
)
@@ -2424,7 +2428,7 @@ def get_bank_cash_account(mode_of_payment, company):
@frappe.whitelist()
def make_maintenance_schedule(source_name, target_doc=None):
def make_maintenance_schedule(source_name: str, target_doc: str | Document | None = None):
doclist = get_mapped_doc(
"Sales Invoice",
source_name,
@@ -2441,7 +2445,7 @@ def make_maintenance_schedule(source_name, target_doc=None):
@frappe.whitelist()
def make_delivery_note(source_name, target_doc=None):
def make_delivery_note(source_name: str, target_doc: Document | None = None):
def set_missing_values(source, target):
target.run_method("set_missing_values")
target.run_method("set_po_nos")
@@ -2490,7 +2494,7 @@ def make_delivery_note(source_name, target_doc=None):
@frappe.whitelist()
def make_sales_return(source_name, target_doc=None):
def make_sales_return(source_name: str, target_doc: Document | None = None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
return make_return_doc("Sales Invoice", source_name, target_doc)
@@ -2584,7 +2588,7 @@ def validate_inter_company_transaction(doc, doctype):
@frappe.whitelist()
def make_inter_company_purchase_invoice(source_name, target_doc=None):
def make_inter_company_purchase_invoice(source_name: str, target_doc: Document | None = None):
return make_inter_company_transaction("Sales Invoice", source_name, target_doc)
@@ -2962,7 +2966,7 @@ def update_address(doc, address_field, address_display_field, address_name):
@frappe.whitelist()
def get_loyalty_programs(customer):
def get_loyalty_programs(customer: str):
"""sets applicable loyalty program to the customer or returns a list of applicable programs"""
from erpnext.selling.doctype.customer.customer import get_loyalty_programs
@@ -2980,7 +2984,7 @@ def get_loyalty_programs(customer):
@frappe.whitelist()
def create_invoice_discounting(source_name, target_doc=None):
def create_invoice_discounting(source_name: str, target_doc: str | Document | None = None):
invoice = frappe.get_doc("Sales Invoice", source_name)
invoice_discounting = frappe.new_doc("Invoice Discounting")
invoice_discounting.company = invoice.company
@@ -3072,7 +3076,9 @@ def get_mode_of_payment_info(mode_of_payment, company):
@frappe.whitelist()
def create_dunning(source_name, target_doc=None, ignore_permissions=False):
def create_dunning(
source_name: str, target_doc: str | Document | None = None, ignore_permissions: bool = False
):
from frappe.model.mapper import get_mapped_doc
def postprocess_dunning(source, target):

View File

@@ -4739,6 +4739,66 @@ class TestSalesInvoice(ERPNextTestSuite):
doc.db_set("do_not_use_batchwise_valuation", original_value)
@change_settings("Selling Settings", {"set_zero_rate_for_expired_batch": True})
def test_zero_valuation_for_standalone_credit_note_with_expired_batch(self):
item_code = "_Test Item for Expiry Batch Zero Valuation"
make_item_for_si(
item_code,
{
"is_stock_item": 1,
"has_batch_no": 1,
"has_expiry_date": 1,
"shelf_life_in_days": 2,
"create_new_batch": 1,
"batch_number_series": "TBATCH-EBZV.####",
},
)
se = make_stock_entry(
item_code=item_code,
qty=10,
target="_Test Warehouse - _TC",
rate=100,
)
# fetch batch no from bundle
batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
si = create_sales_invoice(
posting_date=add_days(nowdate(), 3),
item=item_code,
qty=-10,
rate=100,
is_return=1,
update_stock=1,
use_serial_batch_fields=1,
do_not_save=1,
do_not_submit=1,
)
si.items[0].batch_no = batch_no
si.save()
si.submit()
si.reload()
# check zero incoming rate in voucher
self.assertEqual(si.items[0].incoming_rate, 0.0)
# chekc zero incoming rate in stock ledger
stock_ledger_entry = frappe.db.get_value(
"Stock Ledger Entry",
{
"voucher_type": "Sales Invoice",
"voucher_no": si.name,
"item_code": item_code,
"warehouse": "_Test Warehouse - _TC",
},
["incoming_rate", "valuation_rate"],
as_dict=True,
)
self.assertEqual(stock_ledger_entry.incoming_rate, 0.0)
def make_item_for_si(item_code, properties=None):
from erpnext.stock.doctype.item.test_item import make_item

View File

@@ -52,6 +52,7 @@
"is_free_item",
"apply_tds",
"grant_commission",
"allow_zero_valuation_rate",
"section_break_21",
"net_rate",
"net_amount",
@@ -88,7 +89,6 @@
"serial_and_batch_bundle",
"use_serial_batch_fields",
"col_break5",
"allow_zero_valuation_rate",
"incoming_rate",
"item_tax_rate",
"actual_batch_qty",
@@ -580,6 +580,7 @@
{
"collapsible": 1,
"collapsible_depends_on": "eval:doc.serial_no || doc.batch_no",
"depends_on": "eval:parent.update_stock",
"fieldname": "warehouse_and_reference",
"fieldtype": "Section Break",
"label": "Stock Details"
@@ -595,7 +596,7 @@
"print_hide": 1
},
{
"depends_on": "eval: parent.is_internal_customer && parent.update_stock",
"depends_on": "eval: parent.is_internal_customer",
"fieldname": "target_warehouse",
"fieldtype": "Link",
"hidden": 1,
@@ -613,7 +614,6 @@
"options": "Quality Inspection"
},
{
"depends_on": "eval: doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
"fieldname": "batch_no",
"fieldtype": "Link",
"label": "Batch No",
@@ -626,6 +626,7 @@
},
{
"default": "0",
"depends_on": "eval:parent.update_stock",
"fieldname": "allow_zero_valuation_rate",
"fieldtype": "Check",
"label": "Allow Zero Valuation Rate",
@@ -633,7 +634,6 @@
"print_hide": 1
},
{
"depends_on": "eval: doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
"fieldname": "serial_no",
"fieldtype": "Text",
"label": "Serial No",
@@ -843,6 +843,7 @@
"fieldtype": "Currency",
"label": "Incoming Rate (Costing)",
"no_copy": 1,
"non_negative": 1,
"options": "Company:company:default_currency",
"print_hide": 1
},
@@ -906,7 +907,7 @@
"read_only": 1
},
{
"depends_on": "eval:parent.update_stock == 1 && (doc.use_serial_batch_fields === 0 || doc.docstatus === 1)",
"depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.docstatus === 1",
"fieldname": "serial_and_batch_bundle",
"fieldtype": "Link",
"label": "Serial and Batch Bundle",
@@ -916,7 +917,7 @@
"search_index": 1
},
{
"depends_on": "eval:parent.update_stock === 1",
"depends_on": "eval:doc.use_serial_batch_fields === 0 && doc.docstatus === 0",
"fieldname": "pick_serial_and_batch",
"fieldtype": "Button",
"label": "Pick Serial / Batch No"
@@ -1009,7 +1010,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2025-09-04 11:08:25.583561",
"modified": "2026-02-23 14:37:14.853941",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -26,7 +26,7 @@
},
{
"default": "0",
"depends_on": "eval:parent.doctype == 'Sales Invoice'",
"depends_on": "eval: [\"POS Invoice\", \"Sales Invoice\"].includes(parent.doctype)",
"fieldname": "amount",
"fieldtype": "Currency",
"in_list_view": 1,
@@ -85,14 +85,15 @@
],
"istable": 1,
"links": [],
"modified": "2024-03-27 13:10:36.427565",
"modified": "2026-02-16 20:46:34.592604",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Payment",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -342,14 +342,14 @@ class ShareTransfer(Document):
@frappe.whitelist()
def make_jv_entry(
company,
account,
amount,
payment_account,
credit_applicant_type,
credit_applicant,
debit_applicant_type,
debit_applicant,
company: str,
account: str,
amount: float,
payment_account: str,
credit_applicant_type: str,
credit_applicant: str,
debit_applicant_type: str,
debit_applicant: str,
):
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = "Journal Entry"

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from datetime import date
import frappe
from dateutil import relativedelta
@@ -43,7 +44,13 @@ class SubscriptionPlan(Document):
@frappe.whitelist()
def get_plan_rate(
plan, quantity=1, customer=None, start_date=None, end_date=None, prorate_factor=1, party=None
plan: str,
quantity: int = 1,
customer: str | None = None,
start_date: str | date | None = None,
end_date: str | date | None = None,
prorate_factor: float = 1,
party: str | None = None,
):
plan = frappe.get_doc("Subscription Plan", plan)
if plan.price_determination == "Fixed Rate":

View File

@@ -8,6 +8,8 @@ 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
@@ -83,6 +85,8 @@ class TaxRule(Document):
frappe.throw(_("Tax Template is mandatory."))
def validate_filters(self):
TaxRule = DocType("Tax Rule")
filters = {
"tax_type": self.tax_type,
"customer": self.customer,
@@ -105,37 +109,38 @@ class TaxRule(Document):
"company": self.company,
}
conds = ""
for d in filters:
if conds:
conds += " and "
conds += f"""ifnull({d}, '') = {frappe.db.escape(cstr(filters[d]))}"""
if self.from_date and self.to_date:
conds += f""" and ((from_date > '{self.from_date}' and from_date < '{self.to_date}') or
(to_date > '{self.from_date}' and to_date < '{self.to_date}') or
('{self.from_date}' > from_date and '{self.from_date}' < to_date) or
('{self.from_date}' = from_date and '{self.to_date}' = to_date))"""
elif self.from_date and not self.to_date:
conds += f""" and to_date > '{self.from_date}'"""
elif self.to_date and not self.from_date:
conds += f""" and from_date < '{self.to_date}'"""
tax_rule = frappe.db.sql(
f"select name, priority \
from `tabTax Rule` where {conds} and name != '{self.name}'",
as_dict=1,
query = (
frappe.qb.from_(TaxRule).select(TaxRule.name, TaxRule.priority).where(TaxRule.name != self.name)
)
if tax_rule:
if tax_rule[0].priority == self.priority:
frappe.throw(_("Tax Rule Conflicts with {0}").format(tax_rule[0].name), ConflictingTaxRule)
for field, value in filters.items():
query = query.where(IfNull(TaxRule[field], "") == cstr(value))
if self.from_date and self.to_date:
query = query.where(
((TaxRule.from_date > self.from_date) & (TaxRule.from_date < self.to_date))
| ((TaxRule.to_date > self.from_date) & (TaxRule.to_date < self.to_date))
| ((self.from_date > TaxRule.from_date) & (self.from_date < TaxRule.to_date))
| ((TaxRule.from_date == self.from_date) & (TaxRule.to_date == self.to_date))
)
elif self.from_date:
query = query.where(TaxRule.to_date > self.from_date)
elif self.to_date:
query = query.where(TaxRule.from_date < self.to_date)
tax_rule = query.run(as_dict=True)
if tax_rule and tax_rule[0].priority == self.priority:
frappe.throw(
_("Tax Rule Conflicts with {0}").format(tax_rule[0].name),
ConflictingTaxRule,
)
@frappe.whitelist()
def get_party_details(party, party_type, args=None):
def get_party_details(party: str | None, party_type: str, args: dict | None = None):
out = {}
billing_address, shipping_address = None, None
if args:

View File

@@ -43,16 +43,18 @@
"read_only": 1
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-03-27 13:10:55.008837",
"modified": "2025-11-14 16:17:25.584675",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Transaction Deletion Record Details",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -194,7 +194,7 @@ def get_linked_advances(company, docname):
@frappe.whitelist()
def create_unreconcile_doc_for_selection(selections=None):
def create_unreconcile_doc_for_selection(selections: str | None = None):
if selections:
selections = json.loads(selections)
# assuming each row is a unique voucher

View File

@@ -0,0 +1,41 @@
{
"allow_roles": [
{
"role": "Accounts Manager"
},
{
"role": "Accounts User"
}
],
"creation": "2026-02-22 18:26:42.015787",
"docstatus": 0,
"doctype": "Module Onboarding",
"idx": 4,
"is_complete": 0,
"modified": "2026-02-23 22:51:34.267812",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting Onboarding",
"owner": "Administrator",
"steps": [
{
"step": "Chart of Accounts"
},
{
"step": "Setup Sales taxes"
},
{
"step": "Create Sales Invoice"
},
{
"step": "Create Payment Entry"
},
{
"step": "View Balance Sheet"
},
{
"step": "Review Accounts Settings"
}
],
"title": "Accounting Onboarding"
}

View File

@@ -1,3 +1,43 @@
<h3>{{ _("Fiscal Year") }}</h3>
<h4>{{ _("New Fiscal Year - {0}").format(doc.name) }}</h4>
<p>{{ _("New fiscal year created :- ") }} {{ doc.name }}</p>
<p>{{ _("A new fiscal year has been automatically created.") }}</p>
<p>{{ _("Fiscal Year Details") }}</p>
<table style="margin-bottom: 1rem; width: 70%">
<tr>
<td style="font-weight:bold; width: 40%">{{ _("Year Name") }}</td>
<td>{{ doc.name }}</td>
</tr>
<tr>
<td style="font-weight:bold; width: 40%">{{ _("Start Date") }}</td>
<td>{{ frappe.format_value(doc.year_start_date) }}</td>
</tr>
<tr>
<td style="font-weight:bold; width: 40%">{{ _("End Date") }}</td>
<td>{{ frappe.format_value(doc.year_end_date) }}</td>
</tr>
{% if doc.companies|length > 0 %}
<tr>
<td style="vertical-align: top; font-weight: bold; width: 40%" rowspan="{{ doc.companies|length }}">
{% if doc.companies|length < 2 %}
{{ _("Company") }}
{% else %}
{{ _("Companies") }}
{% endif %}
</td>
<td>{{ doc.companies[0].company }}</td>
</tr>
{% for idx in range(1, doc.companies|length) %}
<tr>
<td>{{ doc.companies[idx].company }}</td>
</tr>
{% endfor %}
{% endif %}
</table>
{% if doc.disabled %}
<p>{{ _("The fiscal year has been automatically created in a Disabled state to maintain consistency with the previous fiscal year's status.") }}</p>
{% endif %}
<p>{{ _("Please review the {0} configuration and complete any required financial setup activities.").format(frappe.utils.get_link_to_form("Fiscal Year", doc.name, frappe.bold("Fiscal Year"))) }}</p>

View File

@@ -1,7 +1,8 @@
{
"attach_print": 0,
"channel": "Email",
"condition": "doc.auto_created",
"condition": "doc.auto_created == 1",
"condition_type": "Python",
"creation": "2018-04-25 14:19:05.440361",
"days_in_advance": 0,
"docstatus": 0,
@@ -11,8 +12,10 @@
"event": "New",
"idx": 0,
"is_standard": 1,
"message": "<h4>{{ _(\"New Fiscal Year - {0}\").format(doc.name) }}</h4>\n\n<p>{{ _(\"A new fiscal year has been automatically created.\") }}</p>\n\n<p>{{ _(\"Fiscal Year Details\") }}</p>\n\n<table style=\"margin-bottom: 1rem; width: 70%\">\n <tr>\n <td style=\"font-weight:bold; width: 40%\">{{ _(\"Year Name\") }}</td>\n <td>{{ doc.name }}</td>\n </tr>\n <tr>\n <td style=\"font-weight:bold; width: 40%\">{{ _(\"Start Date\") }}</td>\n <td>{{ frappe.format_value(doc.year_start_date) }}</td>\n </tr>\n <tr>\n <td style=\"font-weight:bold; width: 40%\">{{ _(\"End Date\") }}</td>\n <td>{{ frappe.format_value(doc.year_end_date) }}</td>\n </tr>\n {% if doc.companies|length > 0 %}\n <tr>\n <td style=\"vertical-align: top; font-weight: bold; width: 40%\" rowspan=\"{{ doc.companies|length }}\">\n {% if doc.companies|length < 2 %}\n {{ _(\"Company\") }}\n {% else %}\n {{ _(\"Companies\") }}\n {% endif %}\n </td>\n <td>{{ doc.companies[0].company }}</td>\n </tr>\n {% for idx in range(1, doc.companies|length) %}\n <tr>\n <td>{{ doc.companies[idx].company }}</td>\n </tr>\n {% endfor %}\n {% endif %}\n</table>\n\n{% if doc.disabled %}\n<p>{{ _(\"The fiscal year has been automatically created in a Disabled state to maintain consistency with the previous fiscal year's status.\") }}</p>\n{% endif %}\n\n<p>{{ _(\"Please review the {0} configuration and complete any required financial setup activities.\").format(frappe.utils.get_link_to_form(\"Fiscal Year\", doc.name, frappe.bold(\"Fiscal Year\"))) }}</p>",
"message_type": "HTML",
"modified": "2023-11-17 08:54:51.532104",
"minutes_offset": 0,
"modified": "2026-02-23 17:37:03.755394",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Notification for new fiscal year",
@@ -27,5 +30,5 @@
],
"send_system_notification": 0,
"send_to_all_assignees": 0,
"subject": "Notification for new fiscal year {{ doc.name }}"
"subject": "New Fiscal Year {{ doc.name }} - Review Required"
}

View File

@@ -0,0 +1,20 @@
{
"action": "Go to Page",
"action_label": "Configure Chart of Accounts",
"creation": "2026-02-22 18:28:15.401383",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 1,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2026-02-23 22:44:45.540780",
"modified_by": "Administrator",
"name": "Chart of Accounts",
"owner": "Administrator",
"path": "Tree/Account",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Review Chart of Accounts",
"validate_action": 1
}

View File

@@ -0,0 +1,21 @@
{
"action": "Create Entry",
"action_label": "Create Payment Entry",
"creation": "2026-02-23 19:22:12.005360",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2026-02-23 20:19:56.482245",
"modified_by": "Administrator",
"name": "Create Payment Entry",
"owner": "Administrator",
"reference_document": "Payment Entry",
"route_options": "{\n \"payment_type\": \"Receive\",\n \"party_type\": \"Customer\"\n}",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Create Payment Entry",
"validate_action": 1
}

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