Compare commits

...

370 Commits

Author SHA1 Message Date
Frappe PR Bot
35ac96f1ec chore(release): Bumped to Version 15.54.5
## [15.54.5](https://github.com/frappe/erpnext/compare/v15.54.4...v15.54.5) (2025-03-19)

### Bug Fixes

* add parenttype condition to payment schedule query in accounts receivable report (backport [#46370](https://github.com/frappe/erpnext/issues/46370)) ([#46499](https://github.com/frappe/erpnext/issues/46499)) ([32335da](32335da839))
* add validation to rename_subcontracting_fields patch ([bc408d9](bc408d979a))
* also consider CRM Deal as party type for ERPNext CRM Integration ([65a80cf](65a80cffe7))
* dashboard link for QC from PR ([426222d](426222d8e0))
* Debit and Credit not equal for Purchase Invoice ([46b6e62](46b6e621c2))
* debit in transaction currency ([8e19b46](8e19b46bd9))
* ensure qty conversion when creating production plan from SO ([8162fb3](8162fb3e5d))
* exclude current doc when checking for duplicate ([b638aed](b638aed758))
* fetch bom_no when updating items in sales order ([41d8b26](41d8b26dd2))
* fetch quality inspection parameter group ([cd0abba](cd0abbae51))
* get bom_no from sales order item and material request item ([e241810](e2418101ab))
* hide subcontracted qty field if PO is not subcontracted ([62feec5](62feec5cc3))
* incorrect production item and bom no in job card ([d071a6c](d071a6c900))
* not able to make PR against stand alone Debit Note ([d62960e](d62960e925))
* not able to select the item in the BOM ([59c653e](59c653ef3f))
* patch ([36ffc2e](36ffc2ee67))
* performance issue for item list view ([34d6e4b](34d6e4bdaa))
* remove duplicate ([e5b2801](e5b2801830))
* repost future sle and gle after capitalization ([#46576](https://github.com/frappe/erpnext/issues/46576)) ([2144f89](2144f89624))
* SABB validation for packed items ([2d6626e](2d6626e906))
* set correct currency for offset account gl entries ([e6dd3f3](e6dd3f3e64))
* set landed cost based on purchase invoice rate ([56bc26a](56bc26aecc))
* set stock adjustment account in difference account ([6202e30](6202e302b1))
* take function call outside loop ([ec1a3a1](ec1a3a1e6b))
* **Transaction Deletion Record:** sql syntax error while fetching lead address ([ea68cae](ea68caec7d))
* UOM conversion error when creating pick list from material transfer request ([2f3dcc2](2f3dcc2137))
* use base currency total ([3e2749d](3e2749d6d5))
* use party explicitly ([5dd5784](5dd5784716))
* use shipping_address_name for address validation in sales invoice ([#46473](https://github.com/frappe/erpnext/issues/46473)) ([38dabdf](38dabdf584))
* using `in` for lookup in list instead of directly assigning ([#46492](https://github.com/frappe/erpnext/issues/46492)) ([950656d](950656d6f7))
* valuation for moving average with batches ([5f1bb1f](5f1bb1f1ba))
* wrong field mapping ([be3e083](be3e083e7d))

### Performance Improvements

* faster count estimation (backport [#46550](https://github.com/frappe/erpnext/issues/46550)) ([#46551](https://github.com/frappe/erpnext/issues/46551)) ([01bab8f](01bab8f22b))
2025-03-19 11:18:07 +00:00
ruthra kumar
187ebaaecd Merge pull request #46582 from frappe/version-15-hotfix
chore: release v15
2025-03-19 16:46:43 +05:30
rohitwaghchaure
a2cb9c1791 Merge pull request #46615 from frappe/mergify/bp/version-15-hotfix/pr-46608
fix: fetch bom_no when updating items in sales order (backport #46608)
2025-03-19 16:31:38 +05:30
rohitwaghchaure
0d8842e387 Merge pull request #46614 from frappe/mergify/bp/version-15-hotfix/pr-46573
Fix set landed cost based on pi (backport #46573)
2025-03-19 16:31:09 +05:30
rohitwaghchaure
38213b31da chore: fix conflicts 2025-03-19 15:47:43 +05:30
Mihir Kandoi
e5b2801830 fix: remove duplicate
(cherry picked from commit 386df968c2)

# Conflicts:
#	erpnext/public/js/utils.js
2025-03-19 10:15:18 +00:00
Mihir Kandoi
41d8b26dd2 fix: fetch bom_no when updating items in sales order
(cherry picked from commit 508727a57a)

# Conflicts:
#	erpnext/public/js/utils.js
2025-03-19 10:15:18 +00:00
rohitwaghchaure
5b802ae527 chore: fix conflicts 2025-03-19 15:35:26 +05:30
rohitwaghchaure
1b8e8e92ae Merge pull request #46611 from frappe/mergify/bp/version-15-hotfix/pr-46595
fix: not able to make PR against stand alone Debit Note (backport #46595)
2025-03-19 15:28:57 +05:30
Mihir Kandoi
ec1a3a1e6b fix: take function call outside loop
(cherry picked from commit b3c400f998)
2025-03-19 09:52:28 +00:00
Mihir Kandoi
36ffc2ee67 fix: patch
(cherry picked from commit 7e669c0728)
2025-03-19 09:52:28 +00:00
Mihir Kandoi
56bc26aecc fix: set landed cost based on purchase invoice rate
(cherry picked from commit 75ab5f2bd0)

# Conflicts:
#	erpnext/patches.txt
2025-03-19 09:52:27 +00:00
Rohit Waghchaure
d62960e925 fix: not able to make PR against stand alone Debit Note
(cherry picked from commit 6a52c30591)
2025-03-19 08:05:33 +00:00
ruthra kumar
c6de50b2a5 Merge pull request #46606 from frappe/mergify/bp/version-15-hotfix/pr-46596
fix: debit in transaction currency (backport #46596)
2025-03-19 12:11:09 +05:30
Rohit Waghchaure
8e19b46bd9 fix: debit in transaction currency
(cherry picked from commit e4acf20a62)
2025-03-19 06:26:26 +00:00
rohitwaghchaure
f6a4855e7f Merge pull request #46594 from frappe/mergify/bp/version-15-hotfix/pr-46593
test: test case for FIFO batch valuation (backport #46593)
2025-03-18 21:23:03 +05:30
Rohit Waghchaure
95718acc9a test: test case for FIFO batch valuation
(cherry picked from commit ad9ac1f058)
2025-03-18 13:50:36 +00:00
rohitwaghchaure
2528acd803 Merge pull request #46592 from frappe/mergify/bp/version-15-hotfix/pr-46588
fix: SABB validation for packed items (backport #46588)
2025-03-18 18:38:50 +05:30
Rohit Waghchaure
2d6626e906 fix: SABB validation for packed items
(cherry picked from commit 3756bf231b)
2025-03-18 12:03:18 +00:00
rohitwaghchaure
a07eb556cf Merge pull request #46584 from frappe/mergify/bp/version-15-hotfix/pr-46575
fix: fetch quality inspection parameter group (backport #46575)
2025-03-18 17:28:33 +05:30
rohitwaghchaure
48edc86845 Merge pull request #46587 from frappe/mergify/bp/version-15-hotfix/pr-46576
fix: repost future sle and gle after capitalization (backport #46576)
2025-03-18 17:27:56 +05:30
rohitwaghchaure
7f5ce4f29d Merge pull request #46585 from frappe/mergify/bp/version-15-hotfix/pr-46554
fix: add validation to rename_subcontracting_fields patch (backport #46554)
2025-03-18 17:27:20 +05:30
rohitwaghchaure
cd7056842d Merge pull request #46589 from frappe/mergify/bp/version-15-hotfix/pr-46579
fix: valuation for moving average with batches (backport #46579)
2025-03-18 17:26:41 +05:30
Rohit Waghchaure
5f1bb1f1ba fix: valuation for moving average with batches
(cherry picked from commit cdfbc73f4c)
2025-03-18 11:05:06 +00:00
rohitwaghchaure
79dacfdef8 Merge pull request #46560 from frappe/mergify/bp/version-15-hotfix/pr-46555
fix: performance issue for item list view (backport #46555)
2025-03-18 16:34:00 +05:30
Khushi Rawat
2144f89624 fix: repost future sle and gle after capitalization (#46576)
(cherry picked from commit 29d77aa19f)
2025-03-18 10:31:47 +00:00
Mihir Kandoi
bc408d979a fix: add validation to rename_subcontracting_fields patch
(cherry picked from commit 6c3117dc0d)
2025-03-18 10:30:41 +00:00
rohitwaghchaure
496f43e7e9 Merge pull request #46556 from frappe/st33974
fix: incorrect production item and bom no in job card
2025-03-18 15:58:26 +05:30
Mihir Kandoi
cd0abbae51 fix: fetch quality inspection parameter group
(cherry picked from commit 0a482c7ea8)
2025-03-18 10:27:30 +00:00
Smit Vora
004ecc53e5 Merge pull request #46578 from frappe/mergify/bp/version-15-hotfix/pr-46515
fix: ensure qty conversion when creating production plan from SO (backport #46515)
2025-03-18 15:00:34 +05:30
Smit Vora
8162fb3e5d fix: ensure qty conversion when creating production plan from SO
(cherry picked from commit 75882cc81c)
2025-03-18 08:56:21 +00:00
rohitwaghchaure
e8047ab2ca chore: fix conflicts 2025-03-18 12:01:11 +05:30
rohitwaghchaure
8dc371dac2 Merge pull request #46558 from frappe/mergify/bp/version-15-hotfix/pr-46553
fix: not able to select the item in the BOM (backport #46553)
2025-03-18 11:59:56 +05:30
rohitwaghchaure
0351faa8c0 Merge pull request #46559 from frappe/mergify/bp/version-15-hotfix/pr-46552
fix: Debit and Credit not equal for Purchase Invoice (backport #46552)
2025-03-18 11:59:38 +05:30
ruthra kumar
7e46845fea Merge pull request #46572 from frappe/mergify/bp/version-15-hotfix/pr-46566
fix: set correct currency for offset account gl entries (backport #46566)
2025-03-18 11:32:05 +05:30
ruthra kumar
e84333e5f2 Merge pull request #46571 from frappe/mergify/bp/version-15-hotfix/pr-46508
fix: use base currency total for UAE VAT 201 report (backport #46508)
2025-03-18 11:24:29 +05:30
ruthra kumar
e6dd3f3e64 fix: set correct currency for offset account gl entries
(cherry picked from commit c32e11e69d)
2025-03-18 05:45:13 +00:00
ruthra kumar
ec43ca97cb test: report ouput on foreign currency PI
(cherry picked from commit e80129627a)
2025-03-18 05:30:10 +00:00
ruthra kumar
3e2749d6d5 fix: use base currency total
(cherry picked from commit 46f4babcd0)
2025-03-18 05:30:09 +00:00
mergify[bot]
01bab8f22b perf: faster count estimation (backport #46550) (#46551)
perf: faster count estimation (#46550)

These count queries themselves take quite a long time. `estimate_count`
uses info_schema stats to guess the time.

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
(cherry picked from commit e47a87839b)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-17 21:23:30 +05:30
Ejaaz Khan
972c96b682 Merge pull request #46565 from frappe/mergify/bp/version-15-hotfix/pr-46564
refactor: remove default print format from sales invoice (backport #46564)
2025-03-17 20:52:46 +05:30
Ejaaz Khan
7cfd7e6539 refactor: remove default print format from sales invoice
(cherry picked from commit f10d1f2b1f)
2025-03-17 14:38:43 +00:00
ruthra kumar
bcd9fd090d Merge pull request #46562 from frappe/mergify/bp/version-15-hotfix/pr-46557
fix(Transaction Deletion Record): sql syntax error while deleting lead address (backport #46557)
2025-03-17 17:00:37 +05:30
venkat102
ea68caec7d fix(Transaction Deletion Record): sql syntax error while fetching lead address
(cherry picked from commit af0d6eeae8)
2025-03-17 11:09:38 +00:00
Rohit Waghchaure
34d6e4bdaa fix: performance issue for item list view
(cherry picked from commit d758fde881)

# Conflicts:
#	erpnext/stock/doctype/item_default/item_default.json
2025-03-17 11:08:38 +00:00
rohitwaghchaure
c7b8514c24 Merge pull request #46549 from frappe/mergify/bp/version-15-hotfix/pr-46504
fix: dashboard link for QC from PR (backport #46504)
2025-03-17 16:37:21 +05:30
Rohit Waghchaure
46b6e621c2 fix: Debit and Credit not equal for Purchase Invoice
(cherry picked from commit ecb31b7c9f)
2025-03-17 11:07:17 +00:00
Rohit Waghchaure
59c653ef3f fix: not able to select the item in the BOM
(cherry picked from commit 96d0cd23f1)
2025-03-17 11:07:16 +00:00
Mihir Kandoi
d071a6c900 fix: incorrect production item and bom no in job card 2025-03-17 16:06:54 +05:30
ruthra kumar
61e126901e Merge pull request #46523 from frappe/revert-46475-mergify/bp/version-15-hotfix/pr-46417
Revert "fix: error when creating delivery note from pick list (backport #46417)"
2025-03-17 15:15:10 +05:30
Rohit Waghchaure
426222d8e0 fix: dashboard link for QC from PR
(cherry picked from commit 551f89f14b)
2025-03-17 04:36:59 +00:00
Sagar Vora
ad24867699 Merge pull request #46534 from frappe/mergify/bp/version-15-hotfix/pr-46533
fix: exclude current doc when checking for duplicate (backport #46533)
2025-03-15 00:37:55 +05:30
Sagar Vora
b638aed758 fix: exclude current doc when checking for duplicate
(cherry picked from commit d8ef5e4d58)
2025-03-14 18:37:47 +00:00
Shariq Ansari
1a9873bc55 Merge pull request #46530 from frappe/mergify/bp/version-15-hotfix/pr-46529
fix: also consider CRM Deal as party type for ERPNext CRM Integration (backport #46529)
2025-03-14 16:07:27 +05:30
Shariq Ansari
65a80cffe7 fix: also consider CRM Deal as party type for ERPNext CRM Integration
(cherry picked from commit 04edbf7efe)
2025-03-14 10:34:11 +00:00
Mihir Kandoi
18e29de0c8 Merge pull request #46527 from frappe/mergify/bp/version-15-hotfix/pr-46513 2025-03-14 13:27:30 +05:30
Mihir Kandoi
e2418101ab fix: get bom_no from sales order item and material request item
(cherry picked from commit ac354505ef)
2025-03-14 07:36:47 +00:00
Mihir Kandoi
62c9181651 Merge pull request #46525 from frappe/mergify/bp/version-15-hotfix/pr-46514
fix: UOM conversion error when creating pick list from material trans… (backport #46514)
2025-03-14 13:04:43 +05:30
Mihir Kandoi
d0008ac6df Merge pull request #46524 from frappe/mergify/bp/version-15-hotfix/pr-46512
fix: hide subcontracted qty field if PO is not subcontracted (backport #46512)
2025-03-14 13:04:10 +05:30
Mihir Kandoi
be3e083e7d fix: wrong field mapping
(cherry picked from commit 8411e2e01f)
2025-03-14 07:13:46 +00:00
Mihir Kandoi
2f3dcc2137 fix: UOM conversion error when creating pick list from material transfer request
(cherry picked from commit 840ea070a9)
2025-03-14 07:13:46 +00:00
Mihir Kandoi
62feec5cc3 fix: hide subcontracted qty field if PO is not subcontracted
(cherry picked from commit 6e8521d761)
2025-03-14 07:12:44 +00:00
Nabin Hait
0852533751 Revert "fix: error when creating delivery note from pick list (backport #46417)" 2025-03-14 12:33:37 +05:30
ruthra kumar
99f59c0410 Merge pull request #46521 from frappe/mergify/bp/version-15-hotfix/pr-46497
fix: use `party` explicitly instead of party_field (backport #46497)
2025-03-14 11:08:05 +05:30
Sanket322
5dd5784716 fix: use party explicitly
(cherry picked from commit 5057e3fe30)
2025-03-14 04:52:33 +00:00
ruthra kumar
5ad3e5b5c8 Merge pull request #46520 from frappe/mergify/bp/version-15-hotfix/pr-46488
refactor: replace get_list with get_all for dynamic link child access (backport #46488)
2025-03-14 10:11:05 +05:30
Sugesh393
7fb26f802c refactor: replace get_list with get_all for dynamic link child access
(cherry picked from commit 8f7f0b81f6)
2025-03-14 04:22:25 +00:00
ruthra kumar
27d6659962 Merge pull request #46480 from frappe/mergify/bp/version-15-hotfix/pr-46473
fix: use shipping_address_name for address validation in sales invoice (backport #46473)
2025-03-14 09:50:37 +05:30
ruthra kumar
3737b4a300 refactor(test): unset billing address 2025-03-14 09:29:43 +05:30
ruthra kumar
1065e483b2 Merge pull request #46510 from aerele/fix/item-stock-difference-account
fix: set stock adjustment account in difference account (backport #45606)
2025-03-14 09:23:46 +05:30
Bhavan23
6202e302b1 fix: set stock adjustment account in difference account 2025-03-13 16:45:18 +05:30
mergify[bot]
2a788a4fb1 refactor: print receipt on order complete on pos (backport #46501) (#46507)
* refactor: print receipt on order complete on pos (#46501)

(cherry picked from commit 0552209310)

# Conflicts:
#	erpnext/selling/page/point_of_sale/pos_past_order_summary.js

* chore: resolve conflict

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-03-13 14:30:38 +05:30
mergify[bot]
32335da839 fix: add parenttype condition to payment schedule query in accounts receivable report (backport #46370) (#46499)
Fix: add parenttype condition to payment schedule query in accounts receivable report (#46370)

fix: add parenttype condition to payment schedule query in accounts receivable report
(cherry picked from commit f311a0fc1c)

Co-authored-by: Shanuka Hewage <89955436+Shanuka-98@users.noreply.github.com>
2025-03-13 12:03:46 +05:30
ruthra kumar
c47cc0572b Merge pull request #46493 from frappe/mergify/bp/version-15-hotfix/pr-46492
fix: using `in` for lookup in list instead of directly assigning (backport #46492)
2025-03-13 08:41:22 +05:30
ruthra kumar
1ec971f805 Merge pull request #46496 from frappe/mergify/bp/version-15/pr-46423
Revert "fix: Show Credit Note amount in credit note column" (backport #46423)
2025-03-13 08:20:53 +05:30
ruthra kumar
a6e92d7d16 Merge pull request #46495 from frappe/mergify/bp/version-15-hotfix/pr-46423
Revert "fix: Show Credit Note amount in credit note column" (backport #46423)
2025-03-13 08:12:37 +05:30
ruthra kumar
4d7071299e Revert "fix: Show Credit Note amount in credit note column"
(cherry picked from commit 5a9767ca67)
2025-03-13 02:35:11 +00:00
ruthra kumar
0223651b5b Revert "fix: Show Credit Note amount in credit note column"
(cherry picked from commit 5a9767ca67)
2025-03-13 02:28:08 +00:00
Sanket Shah
950656d6f7 fix: using in for lookup in list instead of directly assigning (#46492)
fix: using in for lookup in list instead of assigning

Co-authored-by: Sanket322 <shahsanket322003.com>
(cherry picked from commit 38955af802)
2025-03-12 17:08:05 +00:00
Frappe PR Bot
08f47b626c chore(release): Bumped to Version 15.54.4
## [15.54.4](https://github.com/frappe/erpnext/compare/v15.54.3...v15.54.4) (2025-03-12)

### Bug Fixes

* `base_net_rate` Required to Check Valid Range (backport [#46332](https://github.com/frappe/erpnext/issues/46332)) ([#46382](https://github.com/frappe/erpnext/issues/46382)) ([877d5bd](877d5bd3aa))
* **account:** update account number from parent company ([428aedc](428aedc29c))
* Allow rename prospect doctype ([#46352](https://github.com/frappe/erpnext/issues/46352)) ([de46165](de46165768))
* auto email report creation ([#46343](https://github.com/frappe/erpnext/issues/46343)) ([5cc251a](5cc251a172))
* backport translations from develop ([#46428](https://github.com/frappe/erpnext/issues/46428)) ([9c70376](9c703765a1))
* calculate due date based on payment term (backport [#46416](https://github.com/frappe/erpnext/issues/46416)) ([#46479](https://github.com/frappe/erpnext/issues/46479)) ([7f14744](7f147446df))
* change fieldname for cash_flow to export (backport [#46353](https://github.com/frappe/erpnext/issues/46353)) ([#46366](https://github.com/frappe/erpnext/issues/46366)) ([23c4252](23c4252b9b))
* check if set_landed_cost_based_on_purchase_invoice_rate is enabled before running patch ([7047fe2](7047fe2681))
* clear cashe on employee hierarchy change to reflect updated permissions ([4dfdb2b](4dfdb2b0a1))
* consider account freeze date in recalculate_amount_difference_field patch ([8b67527](8b67527900))
* consider stock freeze date in recalculate_amount_difference_field patch ([8264d42](8264d42cd9))
* credit note creation during pos invoice consolidation (backport [#46277](https://github.com/frappe/erpnext/issues/46277)) ([#46469](https://github.com/frappe/erpnext/issues/46469)) ([a4b8b4c](a4b8b4c771))
* do not recalculate qty for batch items during reposting ([bac36f3](bac36f342d))
* doctype name ([1dcbdf3](1dcbdf3257))
* enable no copy for serial no field ([3f9df2f](3f9df2fb2d))
* error in production analytics report ([db6ae61](db6ae61935))
* error when creating delivery note from pick list ([#46417](https://github.com/frappe/erpnext/issues/46417)) ([0b92101](0b921016ff))
* filter batches that going to be zero ([ac97489](ac97489a32))
* incorrect category in list ([002685f](002685fc89))
* make 'company_tax_id' and 'company_fiscal_code' as mandatory ([229f4d3](229f4d3d92))
* not able to save work order with alternative item ([9554a49](9554a49bbd))
* **payment entry:** fetch default bank account based on company (backport [#46379](https://github.com/frappe/erpnext/issues/46379)) ([#46471](https://github.com/frappe/erpnext/issues/46471)) ([1371199](13711993fe))
* pricing rule not ignored in Sales Order ([#46248](https://github.com/frappe/erpnext/issues/46248)) ([8def42f](8def42f751))
* rare precision issue preventing submission of subcontracting order ([6419d02](6419d020a1))
* recalculate_amount_difference_field patch ([f247f02](f247f02e49))
* remove no copy for serial no field of purchase receipt item ([baa564f](baa564fc94))
* rename sla fields patch ([73f11cf](73f11cf19e))
* rename sla fields patch ([#46465](https://github.com/frappe/erpnext/issues/46465)) ([5edbd88](5edbd8851a))
* rename_sla_fields patch ([7bc7557](7bc7557018))
* run bank reconciliation as a background job to prevent request timeout ([739cd18](739cd18604))
* set correct account currency for deferred expence account in PI ([f96848a](f96848a3b9))
* show remaining qty on 'Complete Job' button instead of full qty ([79e6550](79e6550321))
* sla fields patch ([0d044bc](0d044bc5bb))
* stock balance in and out value ([c2001e9](c2001e9c67))
* **test:** incorrect transaction exchange rate in test case ([b76c968](b76c96820e))
* typo in sales_invoice_print ([b610621](b6106212c1))
* uom reverts to default upon selecting do not explode ([#45693](https://github.com/frappe/erpnext/issues/45693)) ([6b1d209](6b1d20970e))
* validate accounting dimension company in Journal Entry & Stock Entry (backport [#46204](https://github.com/frappe/erpnext/issues/46204)) ([#46369](https://github.com/frappe/erpnext/issues/46369)) ([c816f9b](c816f9bd0a))
* validate last_gl_update exists before comparing (backport [#46464](https://github.com/frappe/erpnext/issues/46464)) ([#46468](https://github.com/frappe/erpnext/issues/46468)) ([3cef94e](3cef94e2ed))
* validations and account type filter for `Tax Withholding Category` ([#46207](https://github.com/frappe/erpnext/issues/46207)) ([cc30a01](cc30a01898))
2025-03-12 14:37:29 +00:00
ruthra kumar
0283f7526c Merge pull request #46444 from frappe/version-15-hotfix
chore: release v15
2025-03-12 20:05:56 +05:30
ruthra kumar
3ad451dd6e Merge branch 'version-15' into version-15-hotfix 2025-03-12 19:48:08 +05:30
rohitwaghchaure
9e409bde2e Merge pull request #46486 from frappe/st33357
fix: enable no copy for serial no field
2025-03-12 19:39:32 +05:30
ruthra kumar
8459166323 Merge pull request #46482 from frappe/mergify/bp/version-15-hotfix/pr-46207
fix: validations and account type filter for `Tax Withholding Category` (backport #46207)
2025-03-12 17:46:04 +05:30
ruthra kumar
e1328de712 Merge pull request #46487 from frappe/mergify/bp/version-15-hotfix/pr-46251
fix(bank-reconciliation): run bank reconciliation as a background job (backport #46251)
2025-03-12 17:45:45 +05:30
mergify[bot]
d3a2350b3e fix(invoice):validate return invoice qty (backport #46451) (#46481)
fix(invoice):validate return invoice qty (#46451)

* fix(invoice): validate return quantity when update stock is unchecked

* test: add unit test for validating fully returned invoice quantity

(cherry picked from commit ba96c86576)

Co-authored-by: Bhavansathru <122002510+Bhavan23@users.noreply.github.com>
2025-03-12 17:23:26 +05:30
Bhavan23
739cd18604 fix: run bank reconciliation as a background job to prevent request timeout
(cherry picked from commit a29c6a5aea)
2025-03-12 11:52:26 +00:00
ruthra kumar
e61cc9b12e Merge pull request #46483 from frappe/mergify/bp/version-15-hotfix/pr-46248
fix: pricing rule not ignored in Sales Order (backport #46248)
2025-03-12 17:13:13 +05:30
Mihir Kandoi
baa564fc94 fix: remove no copy for serial no field of purchase receipt item 2025-03-12 17:11:14 +05:30
rohitwaghchaure
a55ec56fbf Merge pull request #46484 from frappe/45440
fix: show remaining qty on 'Complete Job' button instead of full qty
2025-03-12 16:44:57 +05:30
Mihir Kandoi
3f9df2fb2d fix: enable no copy for serial no field 2025-03-12 16:35:50 +05:30
mergify[bot]
7f147446df fix: calculate due date based on payment term (backport #46416) (#46479)
fix: calculate due date based on payment term (#46416)

(cherry picked from commit 9e808c832f)

Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
2025-03-12 16:30:14 +05:30
Sugesh G
8def42f751 fix: pricing rule not ignored in Sales Order (#46248)
* fix: pricing rule not ignored in Sales Order

* test: update parameter do_not_submit to do_not_save

(cherry picked from commit f8c659d8d5)
2025-03-12 10:58:47 +00:00
Mihir Kandoi
79e6550321 fix: show remaining qty on 'Complete Job' button instead of full qty 2025-03-12 16:27:33 +05:30
Priyansh Shah
cc30a01898 fix: validations and account type filter for Tax Withholding Category (#46207)
fix: validations and account type filter for tax withholding category
(cherry picked from commit d371236684)
2025-03-12 10:52:32 +00:00
Sugesh G
38dabdf584 fix: use shipping_address_name for address validation in sales invoice (#46473)
* fix: validate address and contact related to party

* fix: solve unboundlocal error

* refactor: improve variable scope

* refactor: translatable strings

* fix: use shipping_address_name for address validation in sales invoice

* test: add new unit test for address and contact validation

* chore: to avoid keyerror

---------

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
(cherry picked from commit 0bdb81db53)
2025-03-12 10:44:37 +00:00
Ninad Parikh
877d5bd3aa fix: base_net_rate Required to Check Valid Range (backport #46332) (#46382)
fix: `base_net_rate` required to check valid range
2025-03-12 16:12:44 +05:30
Mihir Kandoi
0b53bd3e9a Merge pull request #46475 from frappe/mergify/bp/version-15-hotfix/pr-46417
fix: error when creating delivery note from pick list (backport #46417)
2025-03-12 16:09:50 +05:30
Mihir Kandoi
3606fe8fba Merge pull request #46467 from frappe/mergify/bp/version-15-hotfix/pr-46465
fix: rename sla fields patch (backport #46465)
2025-03-12 16:09:37 +05:30
Mihir Kandoi
0b921016ff fix: error when creating delivery note from pick list (#46417)
(cherry picked from commit 67e9389a02)
2025-03-12 10:00:53 +00:00
rohitwaghchaure
4d49608a68 Merge pull request #46474 from frappe/mergify/bp/version-15-hotfix/pr-46470
fix: do not recalculate qty for batch items during reposting (backport #46470)
2025-03-12 12:39:06 +05:30
Rohit Waghchaure
bac36f342d fix: do not recalculate qty for batch items during reposting
(cherry picked from commit 0753c018d2)
2025-03-12 06:48:00 +00:00
mergify[bot]
a4b8b4c771 fix: credit note creation during pos invoice consolidation (backport #46277) (#46469)
* fix: credit note creation during pos invoice consolidation (#46277)

* fix: credit note creation during pos invoice consolidation

* fix: added check to skip merging empty list of return pos invoices

* fix: sql query

* fix: using return invoice name instead of return invoice object

* fix: added pos invoice field in sales invoice item

(cherry picked from commit 8ba4ac3b86)

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

* chore: resolve conflict

---------

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-03-12 11:41:41 +05:30
mergify[bot]
3cef94e2ed fix: validate last_gl_update exists before comparing (backport #46464) (#46468)
fix: validate last_gl_update exists before comparing (#46464)

(cherry picked from commit 0a5ca0c35f)

Co-authored-by: Dany Robert <rtdany10@gmail.com>
2025-03-12 11:05:24 +05:30
mergify[bot]
13711993fe fix(payment entry): fetch default bank account based on company (backport #46379) (#46471)
fix(payment entry): fetch default bank account based on company (#46379)

(cherry picked from commit b72f6f5a3d)

Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com>
2025-03-12 11:05:08 +05:30
Nabin Hait
24678b0e24 chore: conflicts 2025-03-12 10:38:26 +05:30
Mihir Kandoi
5edbd8851a fix: rename sla fields patch (#46465)
* fix: rename sla fields patch

* fix: rerun patch

(cherry picked from commit 8bce42e633)

# Conflicts:
#	erpnext/patches.txt
2025-03-12 05:04:52 +00:00
rohitwaghchaure
6582e4c5f9 Merge pull request #46462 from frappe/mergify/bp/version-15-hotfix/pr-46455
fix: error in production analytics report (backport #46455)
2025-03-12 09:50:35 +05:30
rohitwaghchaure
fd1c1ba35e Merge pull request #46461 from frappe/mergify/bp/version-15-hotfix/pr-46460
fix: rename sla fields patch (backport #46460)
2025-03-12 09:49:46 +05:30
Mihir Kandoi
db6ae61935 fix: error in production analytics report
(cherry picked from commit 03e66468f6)
2025-03-11 19:00:35 +00:00
Mihir Kandoi
73f11cf19e fix: rename sla fields patch
(cherry picked from commit b6c18849c5)
2025-03-11 18:57:17 +00:00
rohitwaghchaure
8f7dc827ea Merge pull request #46459 from frappe/mergify/bp/version-15-hotfix/pr-46452
fix: sla fields patch (backport #46452)
2025-03-12 00:01:35 +05:30
Mihir Kandoi
0d044bc5bb fix: sla fields patch
(cherry picked from commit d653899372)
2025-03-11 18:08:24 +00:00
rohitwaghchaure
14ee2d239a Merge pull request #46441 from frappe/mergify/bp/version-15-hotfix/pr-46436
fix: filter batches that going to be zero (backport #46436)
2025-03-11 23:38:21 +05:30
Raffael Meyer
fc8eeaf4f6 Merge pull request #46457 from frappe/mergify/bp/version-15-hotfix/pr-46453
ci: ignore PRs labeled with "skip-release-notes" when generating release notes (backport #46453)
2025-03-11 17:51:58 +01:00
barredterra
aad8c88532 ci: ignore PRs labeled with "skip-release-notes" when generating release notes
(cherry picked from commit 57007bf937)
2025-03-11 16:35:20 +00:00
ruthra kumar
e783536ba0 Merge pull request #46450 from frappe/mergify/bp/version-15-hotfix/pr-46398
chore: rename print and stationery account (backport #46398)
2025-03-11 18:12:19 +05:30
chethank1407
ee3feba386 chore: rename print and stationery account
(cherry picked from commit 615997b774)
2025-03-11 12:03:42 +00:00
ruthra kumar
cb9be11448 Merge pull request #46447 from frappe/mergify/bp/version-15-hotfix/pr-46344
fix: make 'company_tax_id' and 'company_fiscal_code' as mandatory (backport #46344)
2025-03-11 17:31:19 +05:30
ruthra kumar
31dc6021e2 chore: translatable strings
(cherry picked from commit 121798ba85)
2025-03-11 11:42:30 +00:00
Bhavan23
229f4d3d92 fix: make 'company_tax_id' and 'company_fiscal_code' as mandatory
(cherry picked from commit abd044eb0d)
2025-03-11 11:42:19 +00:00
ruthra kumar
c25862a85f Merge pull request #46446 from frappe/mergify/bp/version-15-hotfix/pr-45818
fix: set correct account currency for deferred expense account (backport #45818)
2025-03-11 16:50:55 +05:30
vishakhdesai
f96848a3b9 fix: set correct account currency for deferred expence account in PI
(cherry picked from commit 398083853c)
2025-03-11 11:06:14 +00:00
Rohit Waghchaure
ac97489a32 fix: filter batches that going to be zero
(cherry picked from commit aba512c1c6)
2025-03-11 08:02:49 +00:00
ruthra kumar
02ff406b7c Merge pull request #46439 from frappe/mergify/bp/version-15-hotfix/pr-46372
fix(account): update account number from parent company (backport #46372)
2025-03-11 11:19:33 +05:30
venkat102
428aedc29c fix(account): update account number from parent company
(cherry picked from commit 4a4894bc01)
2025-03-11 05:32:15 +00:00
ruthra kumar
6af24dca6e Merge pull request #46432 from frappe/mergify/bp/version-15-hotfix/pr-46427
fix: not able to save work order with alternative item (backport #46414) (backport #46427)
2025-03-11 11:02:04 +05:30
ruthra kumar
8980eb9b9d Merge pull request #46437 from frappe/mergify/bp/version-15-hotfix/pr-46426
fix: clear cashe on employee hierarchy change (backport #46426)
2025-03-11 10:58:50 +05:30
Asmita Hase
4dfdb2b0a1 fix: clear cashe on employee hierarchy change to reflect updated permissions
(cherry picked from commit 6789578b27)
2025-03-11 05:13:04 +00:00
Rohit Waghchaure
9554a49bbd fix: not able to save work order with alternative item
(cherry picked from commit 6ca1f9bc73)
(cherry picked from commit ac7fc608aa)
2025-03-10 17:44:57 +00:00
rohitwaghchaure
5b69445294 Merge pull request #46420 from frappe/mergify/bp/version-15-hotfix/pr-46418
fix: stock balance in and out value (backport #46418)
2025-03-10 23:13:37 +05:30
Raffael Meyer
9c703765a1 fix: backport translations from develop (#46428) 2025-03-10 14:32:19 +01:00
rohitwaghchaure
043539fcdb chore: fix conflicts 2025-03-10 17:37:29 +05:30
ruthra kumar
54a76d8932 Merge pull request #46425 from frappe/mergify/bp/version-15-hotfix/pr-46407
fix: typo in sales_invoice_print (backport #46407)
2025-03-10 16:55:28 +05:30
mahsem
b6106212c1 fix: typo in sales_invoice_print
(cherry picked from commit f7bcae83e4)
2025-03-10 11:18:23 +00:00
Rohit Waghchaure
c2001e9c67 fix: stock balance in and out value
(cherry picked from commit e917bd5334)

# Conflicts:
#	erpnext/stock/report/stock_balance/stock_balance.py
2025-03-10 10:06:09 +00:00
Frappe PR Bot
47429095a2 chore(release): Bumped to Version 15.54.3
## [15.54.3](https://github.com/frappe/erpnext/compare/v15.54.2...v15.54.3) (2025-03-09)

### Bug Fixes

* consider account freeze date in recalculate_amount_difference_field patch ([34f03d6](34f03d608a))
* consider stock freeze date in recalculate_amount_difference_field patch ([a18721d](a18721d21c))
2025-03-09 11:04:11 +00:00
rohitwaghchaure
2f3f87fe7e Merge pull request #46403 from frappe/mergify/bp/version-15/pr-46402
fix: consider account freeze date in recalculate_amount_difference_fi… (backport #46395) (backport #46402)
2025-03-09 16:32:52 +05:30
Mihir Kandoi
2b4dfca3ff chore: resolve conflicts
(cherry picked from commit 985fb5dfdc)
2025-03-08 17:16:43 +00:00
Mihir Kandoi
a18721d21c fix: consider stock freeze date in recalculate_amount_difference_field patch
(cherry picked from commit cd72532789)
(cherry picked from commit 8264d42cd9)
2025-03-08 17:16:43 +00:00
Mihir Kandoi
34f03d608a fix: consider account freeze date in recalculate_amount_difference_field patch
(cherry picked from commit 696f931678)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
(cherry picked from commit 8b67527900)
2025-03-08 17:16:43 +00:00
rohitwaghchaure
b786cd30e6 Merge pull request #46402 from frappe/mergify/bp/version-15-hotfix/pr-46395
fix: consider account freeze date in recalculate_amount_difference_fi… (backport #46395)
2025-03-08 22:45:06 +05:30
Mihir Kandoi
985fb5dfdc chore: resolve conflicts 2025-03-08 22:22:12 +05:30
Mihir Kandoi
8264d42cd9 fix: consider stock freeze date in recalculate_amount_difference_field patch
(cherry picked from commit cd72532789)
2025-03-08 16:28:33 +00:00
Mihir Kandoi
8b67527900 fix: consider account freeze date in recalculate_amount_difference_field patch
(cherry picked from commit 696f931678)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
2025-03-08 16:28:32 +00:00
Frappe PR Bot
2ce8299bc8 chore(release): Bumped to Version 15.54.2
## [15.54.2](https://github.com/frappe/erpnext/compare/v15.54.1...v15.54.2) (2025-03-08)

### Bug Fixes

* incorrect category in list ([3bdd4ce](3bdd4ce116))
* **test:** incorrect transaction exchange rate in test case ([3015628](3015628519))
2025-03-08 07:42:44 +00:00
ruthra kumar
84b03485d6 Merge pull request #46397 from frappe/mergify/bp/version-15/pr-46064
fix: Debit and Credit mismatch on transaction currency debit and credit values (backport #46064)
2025-03-08 13:11:25 +05:30
ruthra kumar
d0b14f1907 chore: resolve conflict 2025-03-08 12:28:49 +05:30
ruthra kumar
cd1803a74d refactor: internal transfer gl
(cherry picked from commit f1d8feec15)
2025-03-08 06:56:37 +00:00
ruthra kumar
a1cf27ec17 test: assert total debit and credit for trx currency
(cherry picked from commit 55d0636123)
2025-03-08 06:56:37 +00:00
ruthra kumar
61880a311a refactor: handle rounding diff for trx currency dr and cr
(cherry picked from commit 455a55b2ce)
2025-03-08 06:56:37 +00:00
ruthra kumar
2d290b153d refactor: trx currency dr and cr for tax rows and item rows
(cherry picked from commit 4cd3f3531c)
2025-03-08 06:56:36 +00:00
ruthra kumar
cacb720556 refactor: convert tax amount using exchange rate
(cherry picked from commit 7528ef147a)
2025-03-08 06:56:36 +00:00
ruthra kumar
3bdd4ce116 fix: incorrect category in list
(cherry picked from commit 6545467aec)
2025-03-08 06:56:36 +00:00
ruthra kumar
c479998cd6 chore: typo
(cherry picked from commit bc792c61e9)
2025-03-08 06:56:36 +00:00
ruthra kumar
501e388186 refactor: isolate to specific doctypes
(cherry picked from commit b348aa3b37)
2025-03-08 06:56:35 +00:00
ruthra kumar
af45ec0d6d refactor(test): save first to let the tax table populate
(cherry picked from commit 23d465805b)
2025-03-08 06:56:35 +00:00
ruthra kumar
3015628519 fix(test): incorrect transaction exchange rate in test case
(cherry picked from commit a31770d122)
2025-03-08 06:56:35 +00:00
ruthra kumar
8b6eea6349 refactor: set transaction currency and rate before gl map
(cherry picked from commit ceca5b4c72)
2025-03-08 06:56:34 +00:00
ruthra kumar
81c29e8f8c refactor: handle payment entry
(cherry picked from commit 5c86e3ce85)
2025-03-08 06:56:34 +00:00
ruthra kumar
04758d3de3 refactor: handle Journal entries
(cherry picked from commit 9f3847c0f8)
2025-03-08 06:56:34 +00:00
ruthra kumar
615b0c40a3 refactor: move utility method to controller
(cherry picked from commit d1d06885dc)
2025-03-08 06:56:34 +00:00
ruthra kumar
231abab321 refactor: set transaction currency dr/cr in sales invoice
(cherry picked from commit 3e292ef2cb)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
2025-03-08 06:56:33 +00:00
ruthra kumar
fff3b1e84e refactor: handle stocked items
(cherry picked from commit ee93ed8c97)
2025-03-08 06:56:33 +00:00
ruthra kumar
5299a1032b refactor: handle stocked items
(cherry picked from commit 7ff3977394)
2025-03-08 06:56:33 +00:00
ruthra kumar
1e5fbc0a48 refactor: set tr currency dr & cr directly on parent document
(cherry picked from commit e9af567033)
2025-03-08 06:56:32 +00:00
ruthra kumar
5c47c35a0f refactor: use highest precision for storing exc rate
(cherry picked from commit b115bf2e2a)
2025-03-08 06:56:32 +00:00
ruthra kumar
2bf910a786 Merge pull request #46396 from frappe/mergify/bp/version-15-hotfix/pr-46064
fix: Debit and Credit mismatch on transaction currency debit and credit values (backport #46064)
2025-03-08 12:25:57 +05:30
ruthra kumar
0b8673777a chore: resolve conflict 2025-03-08 12:11:54 +05:30
ruthra kumar
8f4c1e7169 refactor: internal transfer gl
(cherry picked from commit f1d8feec15)
2025-03-08 06:36:37 +00:00
ruthra kumar
f303245fae test: assert total debit and credit for trx currency
(cherry picked from commit 55d0636123)
2025-03-08 06:36:37 +00:00
ruthra kumar
cd21e5c652 refactor: handle rounding diff for trx currency dr and cr
(cherry picked from commit 455a55b2ce)
2025-03-08 06:36:37 +00:00
ruthra kumar
57e0f73595 refactor: trx currency dr and cr for tax rows and item rows
(cherry picked from commit 4cd3f3531c)
2025-03-08 06:36:37 +00:00
ruthra kumar
2c73e31742 refactor: convert tax amount using exchange rate
(cherry picked from commit 7528ef147a)
2025-03-08 06:36:36 +00:00
ruthra kumar
002685fc89 fix: incorrect category in list
(cherry picked from commit 6545467aec)
2025-03-08 06:36:36 +00:00
ruthra kumar
38a3a43ba5 chore: typo
(cherry picked from commit bc792c61e9)
2025-03-08 06:36:36 +00:00
ruthra kumar
07f938cc10 refactor: isolate to specific doctypes
(cherry picked from commit b348aa3b37)
2025-03-08 06:36:35 +00:00
ruthra kumar
5c013172f9 refactor(test): save first to let the tax table populate
(cherry picked from commit 23d465805b)
2025-03-08 06:36:35 +00:00
ruthra kumar
b76c96820e fix(test): incorrect transaction exchange rate in test case
(cherry picked from commit a31770d122)
2025-03-08 06:36:35 +00:00
ruthra kumar
1d56931050 refactor: set transaction currency and rate before gl map
(cherry picked from commit ceca5b4c72)
2025-03-08 06:36:34 +00:00
ruthra kumar
66dc79ceb5 refactor: handle payment entry
(cherry picked from commit 5c86e3ce85)
2025-03-08 06:36:34 +00:00
ruthra kumar
00cbc89b5f refactor: handle Journal entries
(cherry picked from commit 9f3847c0f8)
2025-03-08 06:36:33 +00:00
ruthra kumar
d7baa451e3 refactor: move utility method to controller
(cherry picked from commit d1d06885dc)
2025-03-08 06:36:33 +00:00
ruthra kumar
e0b5386bf0 refactor: set transaction currency dr/cr in sales invoice
(cherry picked from commit 3e292ef2cb)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
2025-03-08 06:36:33 +00:00
ruthra kumar
133dca1824 refactor: handle stocked items
(cherry picked from commit ee93ed8c97)
2025-03-08 06:36:32 +00:00
ruthra kumar
eb0df50f1b refactor: handle stocked items
(cherry picked from commit 7ff3977394)
2025-03-08 06:36:32 +00:00
ruthra kumar
5761cfb3d5 refactor: set tr currency dr & cr directly on parent document
(cherry picked from commit e9af567033)
2025-03-08 06:36:32 +00:00
ruthra kumar
568b582b6a refactor: use highest precision for storing exc rate
(cherry picked from commit b115bf2e2a)
2025-03-08 06:36:31 +00:00
Shariq Ansari
d115c1c156 Merge pull request #46365 from frappe/mergify/bp/version-15-hotfix/pr-46352
fix: Allow rename prospect doctype (backport #46352)
2025-03-07 17:12:22 +05:30
Nabin Hait
4e688778dc chore: linter issue 2025-03-07 10:28:52 +05:30
Mihir Kandoi
b5890c1d55 Merge pull request #46376 from frappe/mergify/bp/version-15-hotfix/pr-46374 2025-03-06 23:00:13 +05:30
Mihir Kandoi
6419d020a1 fix: rare precision issue preventing submission of subcontracting order
(cherry picked from commit 33b71544db)
2025-03-06 16:46:04 +00:00
Nabin Hait
60364f6dc9 chore: conflicts 2025-03-06 18:14:09 +05:30
mergify[bot]
23c4252b9b fix: change fieldname for cash_flow to export (backport #46353) (#46366)
fix: change fieldname for cash_flow to export (#46353)

fix: change fieldname for cash_flow

Co-authored-by: Sanket322 <shahsanket322003.com>
(cherry picked from commit 606dcb0ad1)

Co-authored-by: Sanket Shah <113279972+Sanket322@users.noreply.github.com>
2025-03-06 18:12:31 +05:30
mergify[bot]
c816f9bd0a fix: validate accounting dimension company in Journal Entry & Stock Entry (backport #46204) (#46369)
fix: validate accounting dimension company in Journal Entry & Stock Entry (#46204)

* fix: validate accounting dimension company in journal entry and stock entry

* test: update test cases to validate company-based accounting dimension

* fix(test): ensure 'Pick List' company matches 'Delivery Note' to prevent test failures

* chore: remove redundant lines of code

(cherry picked from commit 7b6ebad9e6)

Co-authored-by: Bhavansathru <122002510+Bhavan23@users.noreply.github.com>
2025-03-06 18:12:06 +05:30
Shariq Ansari
de46165768 fix: Allow rename prospect doctype (#46352)
fix: allow rename prospect doctype
(cherry picked from commit 884709deb8)

# Conflicts:
#	erpnext/crm/doctype/prospect/prospect.json
2025-03-06 11:50:08 +00:00
Justine Jay
5cc251a172 fix: auto email report creation (#46343)
* fix(financial_statements): mandatory based on filter_based_on value

* fix(financial_statements.js): include options for multiselect
2025-03-06 17:18:26 +05:30
Frappe PR Bot
7c8b34fd8f chore(release): Bumped to Version 15.54.1
## [15.54.1](https://github.com/frappe/erpnext/compare/v15.54.0...v15.54.1) (2025-03-06)

### Bug Fixes

* check if set_landed_cost_based_on_purchase_invoice_rate is enabled before running patch ([12bf31d](12bf31df87))
* recalculate_amount_difference_field patch ([041335f](041335f318))
* rename_sla_fields patch ([bb553c2](bb553c27ab))
2025-03-06 11:05:55 +00:00
rohitwaghchaure
83320c97fa Merge pull request #46360 from frappe/mergify/bp/version-15/pr-46357
fix: rename_sla_fields patch (backport #46355) (backport #46357)
2025-03-06 16:34:33 +05:30
rohitwaghchaure
b004865240 Merge pull request #46362 from frappe/mergify/bp/version-15/pr-46358
fix: recalculate_amount_difference_field patch (backport #46354) (backport #46358)
2025-03-06 16:34:20 +05:30
Mihir Kandoi
525780645a chore: resolve conflicts
(cherry picked from commit 8e65b0ec0c)
2025-03-06 10:42:01 +00:00
Mihir Kandoi
12bf31df87 fix: check if set_landed_cost_based_on_purchase_invoice_rate is enabled before running patch
(cherry picked from commit 95d1976931)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
(cherry picked from commit 7047fe2681)
2025-03-06 10:42:01 +00:00
Mihir Kandoi
041335f318 fix: recalculate_amount_difference_field patch
(cherry picked from commit 0492b941ff)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
(cherry picked from commit f247f02e49)
2025-03-06 10:42:00 +00:00
rohitwaghchaure
9c9c8c9356 Merge pull request #46358 from frappe/mergify/bp/version-15-hotfix/pr-46354
fix: recalculate_amount_difference_field patch (backport #46354)
2025-03-06 14:53:08 +05:30
Mihir Kandoi
8e65b0ec0c chore: resolve conflicts 2025-03-06 14:13:55 +05:30
Mihir Kandoi
bb553c27ab fix: rename_sla_fields patch
(cherry picked from commit e8d4a487c6)
(cherry picked from commit 7bc7557018)
2025-03-06 08:20:30 +00:00
rohitwaghchaure
7080e1422d Merge pull request #46357 from frappe/mergify/bp/version-15-hotfix/pr-46355
fix: rename_sla_fields patch (backport #46355)
2025-03-06 13:48:54 +05:30
Mihir Kandoi
7047fe2681 fix: check if set_landed_cost_based_on_purchase_invoice_rate is enabled before running patch
(cherry picked from commit 95d1976931)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
2025-03-06 07:58:38 +00:00
Mihir Kandoi
f247f02e49 fix: recalculate_amount_difference_field patch
(cherry picked from commit 0492b941ff)

# Conflicts:
#	erpnext/patches/v15_0/recalculate_amount_difference_field.py
2025-03-06 07:58:37 +00:00
Mihir Kandoi
7bc7557018 fix: rename_sla_fields patch
(cherry picked from commit e8d4a487c6)
2025-03-06 07:49:12 +00:00
rohitwaghchaure
e72264f448 Merge pull request #46346 from frappe/mergify/bp/version-15-hotfix/pr-46339
fix: doctype name (backport #46339)
2025-03-05 21:44:40 +05:30
Rohit Waghchaure
1dcbdf3257 fix: doctype name
(cherry picked from commit d039310d80)
2025-03-05 15:48:13 +00:00
Mihir Kandoi
5d6730a059 Merge pull request #46345 from frappe/mergify/bp/version-15-hotfix/pr-45693
fix: uom reverts to default upon selecting do not explode (backport #45693)
2025-03-05 21:09:04 +05:30
Mihir Kandoi
6b1d20970e fix: uom reverts to default upon selecting do not explode (#45693)
* fix: uom reverts to default upon selecting do not explode

* fix: logical error failing tests

(cherry picked from commit 58ed697ba5)
2025-03-05 15:21:58 +00:00
Frappe PR Bot
528107e224 chore(release): Bumped to Version 15.54.0
# [15.54.0](https://github.com/frappe/erpnext/compare/v15.53.4...v15.54.0) (2025-03-05)

### Bug Fixes

* Accounting Period validation throwing for different companies ([6df9cf3](6df9cf327d))
* Add company filter at get_invoice method (backport [#46238](https://github.com/frappe/erpnext/issues/46238)) ([#46299](https://github.com/frappe/erpnext/issues/46299)) ([4f80ddd](4f80ddd834))
* Add permission check in POS's `Toggle Recent Orders` (backport [#46010](https://github.com/frappe/erpnext/issues/46010)) ([#46274](https://github.com/frappe/erpnext/issues/46274)) ([7759775](7759775ee6))
* adding cost center on pos invoice items while applying product discount (backport [#46082](https://github.com/frappe/erpnext/issues/46082)) ([#46322](https://github.com/frappe/erpnext/issues/46322)) ([9a433a6](9a433a6750))
* **asset depreciation schedules:** enable auto commit ([899e468](899e468f6a))
* auto allocation for negative amount outstanding for Customers in Payment Entry ([78a329e](78a329e573))
* Batch Price gets updated only if it is a billed item ([dbd47df](dbd47dff98))
* Batch Price gets updated only if it is a billed item ([8ed512f](8ed512f6c6))
* change voucher_type and voucher_no field type to data ([3a03865](3a03865a8f))
* Close and Reopen buttons dissapear after saving changes ([#46048](https://github.com/frappe/erpnext/issues/46048)) ([506dd3c](506dd3c6b9))
* consider journal entry and return invoice in paid_amount calculation (backport [#46129](https://github.com/frappe/erpnext/issues/46129)) ([#46319](https://github.com/frappe/erpnext/issues/46319)) ([836fd8f](836fd8fbc4))
* consolidate gl entries by project in General Ledger Report (backport [#46314](https://github.com/frappe/erpnext/issues/46314)) ([#46321](https://github.com/frappe/erpnext/issues/46321)) ([6aa8803](6aa8803068))
* Convert tuple of tuples to list of dicts for dot notation access ([#46062](https://github.com/frappe/erpnext/issues/46062)) ([ef19551](ef195513d0))
* delivery note from sales order uom conversion mistake ([d10add4](d10add4b1e))
* depreciation and balances report correction (backport [#46259](https://github.com/frappe/erpnext/issues/46259)) ([#46305](https://github.com/frappe/erpnext/issues/46305)) ([087dde5](087dde5873))
* discount accounting for v15 ([f609012](f609012f02))
* do not include opening invoices in billed items to be received report ([eee500f](eee500f20e))
* don't allow renaming account while system is actively in use (backport [#46176](https://github.com/frappe/erpnext/issues/46176)) ([#46210](https://github.com/frappe/erpnext/issues/46210)) ([faee8d6](faee8d6c5e))
* dont update rate of free item when batch is updated ([9e649d8](9e649d8522))
* error ([61d5680](61d5680c8d))
* exclude already consumed purchase receipt items from asset capitalization (backport [#46329](https://github.com/frappe/erpnext/issues/46329)) ([#46336](https://github.com/frappe/erpnext/issues/46336)) ([6c1ceff](6c1ceff8ee))
* exclude cancelled gl entries ([5e08386](5e083861a4))
* fiscal year error ([7b13d8c](7b13d8cd98))
* fixing test case ([9f4311e](9f4311e7fb))
* if invoice is return then add amount in proper column ([0a65217](0a65217423))
* Include additional account types for Expense Account in LCV (backport [#46206](https://github.com/frappe/erpnext/issues/46206)) ([#46296](https://github.com/frappe/erpnext/issues/46296)) ([88234bb](88234bbf9a))
* incorrect batch picked ([e94f0b1](e94f0b1cca))
* incorrect batch picked in the pick list (backport [#45761](https://github.com/frappe/erpnext/issues/45761)) ([#46315](https://github.com/frappe/erpnext/issues/46315)) ([5a3073c](5a3073c4c1))
* incorrectly billed amount in the purchase receipt ([c247cf8](c247cf888b))
* Naming of Purchase Amount (backport [#46051](https://github.com/frappe/erpnext/issues/46051)) ([#46324](https://github.com/frappe/erpnext/issues/46324)) ([aaf35c5](aaf35c5df9))
* only include submitted docs for internal received quantity validation (backport [#46262](https://github.com/frappe/erpnext/issues/46262)) ([#46304](https://github.com/frappe/erpnext/issues/46304)) ([5ae9faa](5ae9faab91))
* patch ([5e06e4a](5e06e4acce))
* patch ([8f2fdca](8f2fdcae88))
* patch path ([af49f5a](af49f5a8af))
* **patch:** Ensure SLE indexes (backport [#46131](https://github.com/frappe/erpnext/issues/46131)) ([#46135](https://github.com/frappe/erpnext/issues/46135)) ([f3cafef](f3cafef6a7))
* payment entry exchange gain loss issue ([3fb9033](3fb9033fb7))
* pos item detail serial no field (backport [#46211](https://github.com/frappe/erpnext/issues/46211)) ([#46212](https://github.com/frappe/erpnext/issues/46212)) ([de0dfbc](de0dfbca9a))
* pos item selection using serial no (backport [#46200](https://github.com/frappe/erpnext/issues/46200)) ([#46203](https://github.com/frappe/erpnext/issues/46203)) ([0f263bc](0f263bcff2))
* **pos:** get parent item group without user permission ([#46020](https://github.com/frappe/erpnext/issues/46020)) ([29f3aac](29f3aac925))
* production analytics report ([5668795](5668795884))
* rate changing on the deliver note ([75bc68b](75bc68b863))
* rearrange stock settings fields ([87703c6](87703c6511))
* removed mandatory property for the cost center field ([a94292a](a94292a69f))
* rename some sla fields ([c5717b9](c5717b983d))
* replacing serial and batch bundle on pos with auto fetch serial nos (backport [#46236](https://github.com/frappe/erpnext/issues/46236)) ([#46337](https://github.com/frappe/erpnext/issues/46337)) ([1e85f69](1e85f69072))
* **report:** allow `Closed` purchase orders to be visible ([20c4487](20c4487853))
* **report:** allow `Closed` sales orders to be visible ([8799af9](8799af9747))
* **report:** filter sales / purchase orders based on date filters ([2221bf1](2221bf1cba))
* revamp logic (split parent and child) ([7437cea](7437cea458))
* revert last commit ([a09c57f](a09c57f0d1))
* set landed cost based on purchase invoice rate ([fdaf5fa](fdaf5fafda))
* set taxes before calculating taxes and totals ([90dea42](90dea426d8))
* source warehouse not fetched in bom creator ([6157fed](6157fed71c))
* stock qty not recalculate on changing of the qty ([331798b](331798babc))
* stock reservation issue while making Purchase Invoice ([8bd7195](8bd71954f3))
* syntax error ([1790bcc](1790bcc6d1))
* syntax error ([bd48d39](bd48d391e4))
* syntax error ([cc535b7](cc535b7636))
* test case for debit note ([a8b31df](a8b31df65d))
* tests ([46b0734](46b0734d6f))
* tests ([d413039](d41303961c))
* translation DE ([ddcf79d](ddcf79da1d))
* use else instead of unnecessary elif ([6f760d1](6f760d197d))
* use valuation method from settings in stock ageing report (backport [#46068](https://github.com/frappe/erpnext/issues/46068)) ([#46297](https://github.com/frappe/erpnext/issues/46297)) ([d02d005](d02d005913))
* use value from currency exchange when exchange api is disabled (backport [#46137](https://github.com/frappe/erpnext/issues/46137)) ([#46309](https://github.com/frappe/erpnext/issues/46309)) ([ccc0358](ccc0358db6))
* **workspace:** enable is_query_report on purchase reports (backport [#46249](https://github.com/frappe/erpnext/issues/46249)) ([#46306](https://github.com/frappe/erpnext/issues/46306)) ([363129b](363129bcd4))
* Wrong Overdue Status in Sales Invoices (Floating-point arithmetic) (backport [#46146](https://github.com/frappe/erpnext/issues/46146)) ([#46310](https://github.com/frappe/erpnext/issues/46310)) ([1ff0858](1ff085876e))

### Features

* add new Closed and Stopped rows ([7749814](7749814571))
* add total weight in shipment ([#46049](https://github.com/frappe/erpnext/issues/46049)) ([171df3a](171df3aba5))
* create sales invoice print format ([#45403](https://github.com/frappe/erpnext/issues/45403)) ([1a382eb](1a382ebe86))
* **received items to be billed:** add company and date filters (backport [#46271](https://github.com/frappe/erpnext/issues/46271)) ([#46302](https://github.com/frappe/erpnext/issues/46302)) ([400f4f3](400f4f32ad))
* **Sales Invoice:** add items row via "Fetch Timesheet" (backport [#46071](https://github.com/frappe/erpnext/issues/46071)) ([#46311](https://github.com/frappe/erpnext/issues/46311)) ([1c6e464](1c6e4649bd))

### Performance Improvements

* don't track seen for POS Invoice (backport [#46187](https://github.com/frappe/erpnext/issues/46187)) ([#46189](https://github.com/frappe/erpnext/issues/46189)) ([41ab7f3](41ab7f3f7c))
* patch ([ee41e55](ee41e55343))
* replace if function in query ([d0b8e0d](d0b8e0da8d))
2025-03-05 13:31:02 +00:00
ruthra kumar
86b917b04c Merge pull request #46263 from frappe/version-15-hotfix
chore: release v15
2025-03-05 18:59:38 +05:30
ruthra kumar
9875489758 Merge pull request #46340 from frappe/mergify/bp/version-15-hotfix/pr-46020
fix(pos): get parent item group without user permission (backport #46020)
2025-03-05 18:40:04 +05:30
ruthra kumar
939cf321f7 Merge pull request #46338 from frappe/mergify/bp/version-15-hotfix/pr-45403
feat: create sales invoice print format (backport #45403)
2025-03-05 18:39:43 +05:30
Sugesh G
29f3aac925 fix(pos): get parent item group without user permission (#46020)
* fix(pos): get parent item group without user permission

* feat: add item group filter based on user permission

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
(cherry picked from commit 8caf7f275e)
2025-03-05 12:42:03 +00:00
Ejaaz Khan
1a382ebe86 feat: create sales invoice print format (#45403)
* feat: create sales invoice print format

* fix: linter issue

* style: remove border from table

* refactor: change label to uppercase and show taxes

* refactor: format date and add translation on label

* refactor: remove default header and format labels

* refactor: change label style and small fix

* chore: Qty in title case

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
(cherry picked from commit 38aa7cab8a)
2025-03-05 12:38:03 +00:00
mergify[bot]
6c1ceff8ee fix: exclude already consumed purchase receipt items from asset capitalization (backport #46329) (#46336)
* fix: exclude already consumed purchase receipt items from asset capitalization (#46329)

* feat: link purchase receipt row item to capitalization

* fix: avoid fetching already consumed stock and asset items during capitalization

* fix(patch): added patch to link purchase receipt item to stock item child table

* fix: added nosemgrep

* refactor: rename  to

(cherry picked from commit f50d479bfd)

# Conflicts:
#	erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
#	erpnext/patches.txt

* fix: resolved conflicts

* fix: resolved conflicts

---------

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2025-03-05 18:07:14 +05:30
mergify[bot]
1e85f69072 fix: replacing serial and batch bundle on pos with auto fetch serial nos (backport #46236) (#46337)
fix: replacing serial and batch bundle on pos with auto fetch serial nos (#46236)

* fix: replacing serial and batch bundle on pos with auto fetch serial nos

* fix: reserved serial no

added a check to look for serial no in reserved serial nos list before removing it as there might be a situation where an item is returned which was already consolidated.

(cherry picked from commit 35512d40bb)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-03-05 18:00:31 +05:30
rohitwaghchaure
1992c2639c Merge pull request #46333 from frappe/mergify/bp/version-15-hotfix/pr-46330
fix: Accounting Period validation throwing for different companies (backport #46330)
2025-03-05 17:03:24 +05:30
Mihir Kandoi
8f278ab7c7 Merge pull request #46317 from frappe/mergify/bp/version-15-hotfix/pr-45947
fix: set landed cost based on purchase invoice rate (backport #45947)
2025-03-05 16:58:41 +05:30
Mihir Kandoi
7a33bf41d8 Merge pull request #46331 from frappe/mergify/bp/version-15-hotfix/pr-46313
fix: production analytics report (backport #46313)
2025-03-05 16:45:33 +05:30
Rohit Waghchaure
6df9cf327d fix: Accounting Period validation throwing for different companies
(cherry picked from commit b1508efca2)
2025-03-05 11:11:04 +00:00
Mihir Kandoi
5e06e4acce fix: patch 2025-03-05 16:32:26 +05:30
Mihir Kandoi
7749814571 feat: add new Closed and Stopped rows
(cherry picked from commit 6cc3d67835)
2025-03-05 10:54:36 +00:00
Mihir Kandoi
6f760d197d fix: use else instead of unnecessary elif
(cherry picked from commit 961258a4ce)
2025-03-05 10:54:36 +00:00
Mihir Kandoi
5668795884 fix: production analytics report
(cherry picked from commit 772e9ecfaa)
2025-03-05 10:54:36 +00:00
mergify[bot]
aaf35c5df9 fix: Naming of Purchase Amount (backport #46051) (#46324)
fix: Naming of Purchase Amount (#46051)

* fix: Naming of Purchase Amount

* fix: linters

(cherry picked from commit 104f60cc57)

Co-authored-by: 0xD0M1M0 <76812428+0xD0M1M0@users.noreply.github.com>
2025-03-05 16:18:50 +05:30
mergify[bot]
6aa8803068 fix: consolidate gl entries by project in General Ledger Report (backport #46314) (#46321)
fix: consolidate gl entries by project in General Ledger Report (#46314)

(cherry picked from commit 1f685efcaf)

Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
2025-03-05 16:18:42 +05:30
Ben Kebdani
506dd3c6b9 fix: Close and Reopen buttons dissapear after saving changes (#46048)
* fix: Close and Reopen buttons dissapear after saving changes

* style: linter issue

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2025-03-05 15:34:55 +05:30
mergify[bot]
9a433a6750 fix: adding cost center on pos invoice items while applying product discount (backport #46082) (#46322)
fix: adding cost center on pos invoice items while applying product discount (#46082)

(cherry picked from commit 926e4ecc4f)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-03-05 15:22:46 +05:30
mergify[bot]
e3ce17bd6e fix:[regional] Italian einvoice xml generated with wrong prices (#40254) (backport #45840) (#46318)
Merge pull request #45840 from gms-electronics/40254-italian-einvoice

fix:[regional] Italian einvoice xml generated with wrong prices (#40254)
(cherry picked from commit c5f90c823d)

Co-authored-by: Fab <fabian.thobe@mwv.vc>
2025-03-05 15:19:41 +05:30
mergify[bot]
836fd8fbc4 fix: consider journal entry and return invoice in paid_amount calculation (backport #46129) (#46319)
fix: consider journal entry and return invoice in paid_amount calculation (#46129)

* fix: consider journal entry and return invoice in paid_amount calculation

* test: add new unit test to consider journal entry and return invoice in paid_amount calculation

(cherry picked from commit 425fb12e91)

Co-authored-by: Sugesh G <73237300+Sugesh393@users.noreply.github.com>
2025-03-05 15:07:36 +05:30
Mihir Kandoi
fe8c9a3605 chore: resolve conflicts 2025-03-05 14:54:40 +05:30
Mihir Kandoi
941d67a0b6 chore: resolve conflicts 2025-03-05 14:48:41 +05:30
mergify[bot]
5a3073c4c1 fix: incorrect batch picked in the pick list (backport #45761) (#46315)
fix: incorrect batch picked in the pick list

(cherry picked from commit e1b7688a17)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2025-03-05 14:48:01 +05:30
Mihir Kandoi
8f2fdcae88 fix: patch
(cherry picked from commit 1230127d24)

# Conflicts:
#	erpnext/patches.txt
2025-03-05 09:02:36 +00:00
Mihir Kandoi
a09c57f0d1 fix: revert last commit
(cherry picked from commit 154e9813c4)

# Conflicts:
#	erpnext/patches.txt
2025-03-05 09:02:35 +00:00
Mihir Kandoi
7b13d8cd98 fix: fiscal year error
(cherry picked from commit 7cf8e498c4)
2025-03-05 09:02:34 +00:00
Mihir Kandoi
ee41e55343 perf: patch
(cherry picked from commit a41024813b)
2025-03-05 09:02:34 +00:00
Mihir Kandoi
fdaf5fafda fix: set landed cost based on purchase invoice rate
(cherry picked from commit 17d415b105)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
#	erpnext/patches.txt
#	erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
2025-03-05 09:02:34 +00:00
rohitwaghchaure
c85fd36960 Merge pull request #46308 from frappe/mergify/bp/version-15-hotfix/pr-45987
fix: rename some sla fields (backport #45987)
2025-03-05 14:30:54 +05:30
rohitwaghchaure
a4e5a46566 Merge pull request #46298 from frappe/mergify/bp/version-15-hotfix/pr-46046
perf: optimize query in project.py (backport #46046)
2025-03-05 14:30:26 +05:30
ruthra kumar
3c39888227 Merge pull request #46069 from frappe/mergify/bp/version-15-hotfix/pr-46049
feat: add total weight in shipment (backport #46049)
2025-03-05 14:23:49 +05:30
mergify[bot]
d5f07f06c7 refactor: rename subcontracting fields (backport #46226) (#46301)
* refactor: rename subcontracting fields

(cherry picked from commit b4f65154f5)

# Conflicts:
#	erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json

* chore: resolve conflicts

---------

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2025-03-05 14:08:10 +05:30
mergify[bot]
ccc0358db6 fix: use value from currency exchange when exchange api is disabled (backport #46137) (#46309)
fix: use value from currency exchange when exchange api is disabled (#46137)

(cherry picked from commit 2d26bff870)

Co-authored-by: Venkatesh <47534423+venkat102@users.noreply.github.com>
2025-03-05 14:07:12 +05:30
mergify[bot]
1ff085876e fix: Wrong Overdue Status in Sales Invoices (Floating-point arithmetic) (backport #46146) (#46310)
fix: Wrong Overdue Status in Sales Invoices (Floating-point arithmetic) (#46146)

* fix: Wrong Overdue Status in Sales Invoices (Floating-point arithmetic)

* style: after run pre-commit

(cherry picked from commit 89bcdd6fa5)

Co-authored-by: Diógenes Souza <103958767+devdiogenes@users.noreply.github.com>
2025-03-05 14:07:03 +05:30
mergify[bot]
1c6e4649bd feat(Sales Invoice): add items row via "Fetch Timesheet" (backport #46071) (#46311)
feat(Sales Invoice): add items row via "Fetch Timesheet" (#46071)

(cherry picked from commit 94547188bf)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-03-05 14:06:55 +05:30
Mihir Kandoi
e4e1be568b Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr-45987 2025-03-05 13:59:59 +05:30
Mihir Kandoi
01db730714 Merge pull request #46300 from frappe/mergify/bp/version-15-hotfix/pr-46258
fix: delivery note from sales order uom conversion mistake (backport #46258)
2025-03-05 13:59:14 +05:30
ruthra kumar
dbcffa7ea4 Merge pull request #46235 from frappe/mergify/bp/version-15-hotfix/pr-46223
fix: dont update rate of free item when batch is updated (backport #46223)
2025-03-05 13:57:23 +05:30
ruthra kumar
36fa6bf15c chore: resolve conflict 2025-03-05 13:55:50 +05:30
mergify[bot]
4f80ddd834 fix: Add company filter at get_invoice method (backport #46238) (#46299)
fix: Add company filter at get_invoice method (#46238)

(cherry picked from commit a8d1cbc1c3)

Co-authored-by: Kunhi <kunhimohamed6@gmail.com>
2025-03-05 13:12:20 +05:30
mergify[bot]
400f4f32ad feat(received items to be billed): add company and date filters (backport #46271) (#46302)
feat(received items to be billed): add company and date filters (#46271)

* feat(received items to be billed): add company and date filters

* feat(delivered to be billed): add company and date filters

* feat: add company and date conditions

* chore: remove debugger

(cherry picked from commit 6117706ab5)

Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com>
2025-03-05 13:12:09 +05:30
mergify[bot]
363129bcd4 fix(workspace): enable is_query_report on purchase reports (backport #46249) (#46306)
fix(workspace): enable is_query_report on purchase reports (#46249)

* fix(workspace): enable is_query_report on purchase reports

* fix: resolved conflict

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
(cherry picked from commit 5513e24b00)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2025-03-05 13:11:53 +05:30
mergify[bot]
087dde5873 fix: depreciation and balances report correction (backport #46259) (#46305)
fix: depreciation and balances report correction (#46259)

(cherry picked from commit 4a542b22a4)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2025-03-05 13:11:45 +05:30
mergify[bot]
5ae9faab91 fix: only include submitted docs for internal received quantity validation (backport #46262) (#46304)
fix: only include submitted docs for internal received quantity validation (#46262)

(cherry picked from commit 88fcdbb81e)

Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com>
2025-03-05 13:11:34 +05:30
Mihir Kandoi
446a8fe096 chore: resolve conflicts 2025-03-05 13:08:47 +05:30
Mihir Kandoi
0b50f1a9c3 chore: fix pre-commit/linter error 2025-03-05 12:57:53 +05:30
Mihir Kandoi
f29c43811c chore: resolve conflicts 2025-03-05 12:55:10 +05:30
Mihir Kandoi
e4fbd22173 Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr-46223 2025-03-05 12:51:01 +05:30
Mihir Kandoi
6b56724436 chore: resolve conflicts 2025-03-05 12:46:52 +05:30
Mihir Kandoi
af49f5a8af fix: patch path
(cherry picked from commit dcec446e55)

# Conflicts:
#	erpnext/patches.txt
2025-03-05 07:16:48 +00:00
Mihir Kandoi
46b0734d6f fix: tests
(cherry picked from commit 019303dd12)

# Conflicts:
#	erpnext/support/doctype/issue/issue.json
2025-03-05 07:16:47 +00:00
Mihir Kandoi
d41303961c fix: tests
(cherry picked from commit 1b831e9abd)
2025-03-05 07:16:47 +00:00
Mihir Kandoi
c5717b983d fix: rename some sla fields
(cherry picked from commit baa0dd1235)

# Conflicts:
#	erpnext/patches.txt
2025-03-05 07:16:46 +00:00
mergify[bot]
d02d005913 fix: use valuation method from settings in stock ageing report (backport #46068) (#46297)
fix: use valuation method from settings in stock ageing report

(cherry picked from commit da09c278c8)

Co-authored-by: Mihir Kandoi <kandoimihir@gmail.com>
2025-03-05 12:40:13 +05:30
Mihir Kandoi
d10add4b1e fix: delivery note from sales order uom conversion mistake
(cherry picked from commit 49a43d355d)

# Conflicts:
#	erpnext/selling/doctype/sales_order/sales_order.py
2025-03-05 06:52:07 +00:00
mergify[bot]
88234bbf9a fix: Include additional account types for Expense Account in LCV (backport #46206) (#46296)
fix: Include additional account types for Expense Account in LCV (#46206)

fix: additional account types in filters for the Expense account selection
(cherry picked from commit 59e99f167d)

Co-authored-by: Priyansh Shah <108476017+priyanshshah2442@users.noreply.github.com>
2025-03-05 12:19:31 +05:30
Mihir Kandoi
d8a1d0e908 test: added test
(cherry picked from commit 6073f5a6f9)
2025-03-05 06:41:09 +00:00
Mihir Kandoi
7437cea458 fix: revamp logic (split parent and child)
(cherry picked from commit f7594e2ff9)
2025-03-05 06:41:08 +00:00
Mihir Kandoi
1790bcc6d1 fix: syntax error
(cherry picked from commit 2f1e253e19)
2025-03-05 06:41:08 +00:00
Mihir Kandoi
d0b8e0da8d perf: replace if function in query
(cherry picked from commit 5e66231ca4)
2025-03-05 06:41:08 +00:00
rohitwaghchaure
ab4b1d4356 Merge pull request #46019 from frappe/st30783
fix: source warehouse not fetched in bom creator
2025-03-05 12:06:31 +05:30
mergify[bot]
7759775ee6 fix: Add permission check in POS's Toggle Recent Orders (backport #46010) (#46274)
fix: use get_list to check permissions

(cherry picked from commit a08bc6b913)

Co-authored-by: Sanket322 <shahsanket322003.com>
2025-03-05 12:04:17 +05:30
mergify[bot]
1b00de1815 chore: erpnext.com -> frappe.io/erpnext (backport #46288) (#46290)
* chore: erpnext.com -> frappe.io/erpnext (#46288)

(cherry picked from commit 41fe30ea6e)

# Conflicts:
#	README.md

* Update README.md

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-05 11:35:52 +05:30
mergify[bot]
41ab7f3f7c perf: don't track seen for POS Invoice (backport #46187) (#46189)
* perf: don't track seen for POS Invoice (#46187)

This is a moving doctype. Do people even browse the list view?

It doesn't make much sense, either. POS INvoices are rarely "reviewed" by multiple users.

(cherry picked from commit ded0aab680)

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

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-05 11:28:22 +05:30
ruthra kumar
5049f80b7f Merge pull request #46278 from mhh008/version-15-hotfix
fix-de-transaltion
2025-03-05 11:01:42 +05:30
rohitwaghchaure
d650b23722 Merge pull request #46282 from frappe/mergify/bp/version-15-hotfix/pr-46279
fix: rate changing on the deliver note (backport #46279)
2025-03-04 23:24:30 +05:30
Rohit Waghchaure
75bc68b863 fix: rate changing on the deliver note
(cherry picked from commit 6f40849d55)
2025-03-04 16:12:37 +00:00
rohitwaghchaure
f37fe1781a Merge pull request #46280 from frappe/mergify/bp/version-15-hotfix/pr-46208
fix: stock reservation issue while making Purchase Invoice (backport #46208)
2025-03-04 21:41:53 +05:30
Rohit Waghchaure
8bd71954f3 fix: stock reservation issue while making Purchase Invoice
(cherry picked from commit 64985bffe0)
2025-03-04 14:41:41 +00:00
Lakshit Jain
0f86ed28bc Merge pull request #46276 from frappe/mergify/bp/version-15-hotfix/pr-46039
fix: Ensure new line is added regardless of postal code presence (backport #46039)
2025-03-04 19:14:17 +05:30
mhh008
ddcf79da1d fix: translation DE 2025-03-04 13:56:19 +01:00
Lakshit Jain
b5fcd682a6 Merge pull request #46268 from frappe/mergify/bp/version-15-hotfix/pr-45896
fix: auto allocation for negative amount outstanding for Customers in Payment Entry (backport #45896)
2025-03-04 18:17:15 +05:30
Lakshit Jain
29405498bd Merge pull request #46270 from frappe/mergify/bp/version-15-hotfix/pr-46260
fix: do not include opening invoices in billed items to be received report (backport #46260)
2025-03-04 18:16:42 +05:30
Sanket322
1630979f05 refactor: add new line ragardless of postal code
(cherry picked from commit 746adfd057)
2025-03-04 12:36:40 +00:00
Smit Vora
6ab7d98681 Merge pull request #46139 from frappe/mergify/bp/version-15-hotfix/pr-46117
fix(report): Allow `Closed` Purchase Orders to be Visible in Purchase Order Analysis Report (backport #46117)
2025-03-04 18:03:47 +05:30
ljain112
eee500f20e fix: do not include opening invoices in billed items to be received report
(cherry picked from commit c1ddf444c6)
2025-03-04 11:24:24 +00:00
ljain112
78a329e573 fix: auto allocation for negative amount outstanding for Customers in Payment Entry
(cherry picked from commit 6275b44a0b)
2025-03-04 11:20:12 +00:00
Smit Vora
83dcbec86a chore: resolve conflicts 2025-03-04 16:43:55 +05:30
Smit Vora
5b6ed1d077 Merge pull request #46253 from frappe/mergify/bp/version-15-hotfix/pr-46062
fix: Convert tuple of tuples to list of dicts for dot notation access (backport #46062)
2025-03-04 16:39:16 +05:30
Smit Vora
284c93ecc2 Merge pull request #46246 from frappe/mergify/bp/version-15-hotfix/pr-46192
fix: Exclude Cancelled GL Entries (backport #46192)
2025-03-04 16:38:38 +05:30
Smit Vora
33051ec17e Merge pull request #46245 from frappe/mergify/bp/version-15-hotfix/pr-45972
fix: Set Taxes Before Calculating Taxes and Totals (backport #45972)
2025-03-04 16:38:24 +05:30
ruthra kumar
9e54e2ea58 Merge pull request #46266 from frappe/mergify/bp/version-15-hotfix/pr-45751
fix: change voucher_type and voucher_name field type to data (backport #45751)
2025-03-04 16:12:40 +05:30
ruthra kumar
c09c4cc243 Merge pull request #46252 from frappe/mergify/bp/version-15-hotfix/pr-46070
fix: Batch Price gets updated only if it is a billed item (backport #46070)
2025-03-04 15:49:25 +05:30
ruthra kumar
489efda985 chore: resolve conflict 2025-03-04 15:47:03 +05:30
Sugesh393
3a03865a8f fix: change voucher_type and voucher_no field type to data
(cherry picked from commit f8ab021920)

# Conflicts:
#	erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json
2025-03-04 10:10:54 +00:00
ruthra kumar
854632dd51 chore: resolve conflict 2025-03-04 15:03:03 +05:30
ruthra kumar
ef44ad79a0 Merge pull request #46261 from frappe/mergify/bp/version-15-hotfix/pr-46172
fix: Show Credit Note amount in credit note column (backport #46172)
2025-03-04 15:00:15 +05:30
Sanket322
a8b31df65d fix: test case for debit note
(cherry picked from commit 6719bbeb10)
2025-03-04 07:58:31 +00:00
Sanket322
9f4311e7fb fix: fixing test case
(cherry picked from commit 9b2b477ae0)
2025-03-04 07:58:31 +00:00
Sanket322
0a65217423 fix: if invoice is return then add amount in proper column
(cherry picked from commit ccb4bdbe4c)
2025-03-04 07:58:30 +00:00
Sanket Shah
ef195513d0 fix: Convert tuple of tuples to list of dicts for dot notation access (#46062)
fix: use as_dict to convert tuples into list of dict

Co-authored-by: Sanket322 <shahsanket322003.com>
(cherry picked from commit e4b0ab6656)
2025-03-03 16:09:45 +00:00
mergify[bot]
f3cafef6a7 fix(patch): Ensure SLE indexes (backport #46131) (#46135)
* fix(patch): Ensure SLE indexes (#46131)

Because of the way this change was pushed in parts, some sites don't see
this as "update" and don't have the new indexes.

(cherry picked from commit f62aa8fc57)

# Conflicts:
#	erpnext/patches.txt

* fix: resolved conflict

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2025-03-03 21:37:02 +05:30
ruthra kumar
6762dc3392 chore: linter fix
(cherry picked from commit 0a2193e458)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 14:27:25 +00:00
Nirmalrajaa K
dbd47dff98 fix: Batch Price gets updated only if it is a billed item
(cherry picked from commit 1a56b83054)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 14:27:25 +00:00
Nirmalrajaa K
8ed512f6c6 fix: Batch Price gets updated only if it is a billed item
(cherry picked from commit 9597b1a69e)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 14:27:25 +00:00
Marica
3a6ef2564e Merge pull request #46073 from 0xD0M1M0/fix-discount-accounting-v15
fix: discount accounting for v15
2025-03-03 19:46:51 +05:30
Smit Vora
35df539da3 chore: fix linters 2025-03-03 17:02:15 +05:30
Ninad1306
5e083861a4 fix: exclude cancelled gl entries
(cherry picked from commit 3251a331dd)
2025-03-03 11:28:57 +00:00
Ninad1306
41e9a10ab4 test: validate fetching of taxes based on taxes and charges template
(cherry picked from commit 196ef7ac4e)
2025-03-03 11:27:45 +00:00
Ninad1306
90dea426d8 fix: set taxes before calculating taxes and totals
(cherry picked from commit 0fd0695bbb)
2025-03-03 11:27:45 +00:00
Mihir Kandoi
bd48d391e4 fix: syntax error 2025-03-03 16:40:19 +05:30
rohitwaghchaure
9082ff6aa9 Merge pull request #46242 from frappe/mergify/bp/version-15-hotfix/pr-46239
fix: incorrectly billed amount in the purchase receipt (backport #46239)
2025-03-03 16:16:09 +05:30
rohitwaghchaure
ba0ce267c2 Merge pull request #46243 from frappe/mergify/bp/version-15-hotfix/pr-46231
fix: incorrect batch picked (backport #46231)
2025-03-03 16:15:43 +05:30
Mihir Kandoi
cc535b7636 fix: syntax error 2025-03-03 15:27:35 +05:30
Mihir Kandoi
81c7b8c273 chore: resolve conflicts 2025-03-03 15:05:44 +05:30
Rohit Waghchaure
e94f0b1cca fix: incorrect batch picked
(cherry picked from commit d2564cad68)
2025-03-03 09:24:24 +00:00
Rohit Waghchaure
c247cf888b fix: incorrectly billed amount in the purchase receipt
(cherry picked from commit a5271fdb2e)
2025-03-03 09:15:48 +00:00
Khushi Rawat
7409c140d4 Merge pull request #46237 from frappe/mergify/bp/version-15-hotfix/pr-46201
fix(asset depreciation schedules): enable auto commit (backport #46201)
2025-03-03 14:04:39 +05:30
Mihir Kandoi
7f4d553201 chore: resolve conflicts 2025-03-03 12:28:06 +05:30
venkat102
899e468f6a fix(asset depreciation schedules): enable auto commit
(cherry picked from commit a4b24f7451)
2025-03-03 06:42:02 +00:00
mergify[bot]
faee8d6c5e fix: don't allow renaming account while system is actively in use (backport #46176) (#46210)
fix: don't allow renaming account while system is actively in use (#46176)

(cherry picked from commit 999f1cf96d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2025-03-03 11:16:37 +05:30
Frappe PR Bot
171f966421 chore(release): Bumped to Version 15.53.4
## [15.53.4](https://github.com/frappe/erpnext/compare/v15.53.3...v15.53.4) (2025-03-03)

### Bug Fixes

* removed mandatory property for the cost center field ([8eddc09](8eddc09bba))
2025-03-03 05:12:02 +00:00
rohitwaghchaure
3f76a413f8 Merge pull request #46205 from frappe/mergify/bp/version-15/pr-46186
fix: removed mandatory property for the cost center field (backport #46186)
2025-03-03 10:40:35 +05:30
Mihir Kandoi
61d5680c8d fix: error
(cherry picked from commit 7c9c0c7776)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 04:46:28 +00:00
Mihir Kandoi
9e649d8522 fix: dont update rate of free item when batch is updated
(cherry picked from commit a3596f717b)

# Conflicts:
#	erpnext/stock/get_item_details.py
2025-03-03 04:46:28 +00:00
rohitwaghchaure
c0f736736e Merge pull request #46217 from frappe/mergify/bp/version-15-hotfix/pr-46202
fix: rearrange stock settings fields (backport #46202)
2025-03-03 10:15:36 +05:30
Frappe PR Bot
bcd02df6fd chore(release): Bumped to Version 15.53.3
## [15.53.3](https://github.com/frappe/erpnext/compare/v15.53.2...v15.53.3) (2025-03-03)

### Bug Fixes

* stock qty not recalculate on changing of the qty ([9186f13](9186f13458))
2025-03-03 04:02:37 +00:00
rohitwaghchaure
7a71c24d5c Merge pull request #46232 from frappe/mergify/bp/version-15/pr-46224
fix: stock qty not recalculate on changing of the qty (backport #46219) (backport #46224)
2025-03-03 09:31:16 +05:30
Rohit Waghchaure
9186f13458 fix: stock qty not recalculate on changing of the qty
(cherry picked from commit 464e3339fe)
(cherry picked from commit 331798babc)
2025-03-03 03:57:22 +00:00
rohitwaghchaure
c48e157a25 Merge pull request #46224 from frappe/mergify/bp/version-15-hotfix/pr-46219
fix: stock qty not recalculate on changing of the qty (backport #46219)
2025-03-03 08:24:05 +05:30
mergify[bot]
84ca0ada1b refactor: using function to unset grand total to default mode of payment in pos (backport #46228) (#46229)
refactor: using function to unset grand total to default mode of payment in pos (#46228)

(cherry picked from commit 62c3915ecb)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-03-03 01:59:13 +05:30
Rohit Waghchaure
331798babc fix: stock qty not recalculate on changing of the qty
(cherry picked from commit 464e3339fe)
2025-03-02 06:46:56 +00:00
rohitwaghchaure
93fed0ce86 chore: fix conflicts 2025-03-01 12:53:44 +05:30
Rohit Waghchaure
87703c6511 fix: rearrange stock settings fields
(cherry picked from commit 93f461c6f3)

# Conflicts:
#	erpnext/stock/doctype/stock_settings/stock_settings.json
2025-03-01 06:57:35 +00:00
mergify[bot]
de0dfbca9a fix: pos item detail serial no field (backport #46211) (#46212)
fix: pos item detail serial no field (#46211)

(cherry picked from commit d2fad44e89)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-28 23:24:49 +05:30
Rohit Waghchaure
8eddc09bba fix: removed mandatory property for the cost center field
(cherry picked from commit 079cf772aa)
2025-02-28 10:25:50 +00:00
mergify[bot]
0f263bcff2 fix: pos item selection using serial no (backport #46200) (#46203)
fix: pos item selection using serial no (#46200)

(cherry picked from commit 8fb09decd2)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-28 15:43:59 +05:30
ruthra kumar
a9b2f4885e Merge pull request #46197 from frappe/mergify/bp/version-15-hotfix/pr-46186
fix: removed mandatory property for the cost center field (backport #46186)
2025-02-28 13:56:09 +05:30
Rohit Waghchaure
a94292a69f fix: removed mandatory property for the cost center field
(cherry picked from commit 079cf772aa)
2025-02-28 08:03:51 +00:00
Sagar Vora
5b072c88a6 Merge pull request #46191 from frappe/mergify/bp/version-15-hotfix/pr-46177
fix: Payment Entry fixes (backport #46177)
2025-02-28 10:44:00 +05:30
vishakhdesai
3fb9033fb7 fix: payment entry exchange gain loss issue
(cherry picked from commit 2dbef23244)
2025-02-28 05:13:43 +00:00
Frappe PR Bot
d9c1b58fc3 chore(release): Bumped to Version 15.53.2
## [15.53.2](https://github.com/frappe/erpnext/compare/v15.53.1...v15.53.2) (2025-02-26)

### Bug Fixes

* check value as int ([97d3e86](97d3e8648b))
* enable fetch_timesheet_in_sales_invoice in test ([feb64cb](feb64cb9b5))
* incorrect stock value difference for adjustment entry ([96d44e3](96d44e362d))
* inventory dimension for maintence visit ([ec3b281](ec3b281a3b))
* no permission to get project settings in sales invoice ([b8281c3](b8281c34e2))
* pos opening entry dialog not saving on change data (backport [#46066](https://github.com/frappe/erpnext/issues/46066)) ([#46067](https://github.com/frappe/erpnext/issues/46067)) ([0ae2d61](0ae2d61974))
* **project settings:** add checkbox to auto fetch timesheet in sales invoice ([b3c1df8](b3c1df8561))
* **sales invoice:** check fetch_timesheet_in_sales_invoice enabled before fetching the timesheet ([b1095bb](b1095bb91b))
* valuation rate for batch ([c72dab4](c72dab49f4))
2025-02-26 12:58:53 +00:00
ruthra kumar
f93d1a2633 Merge pull request #46132 from frappe/version-15-hotfix
chore: release v15
2025-02-26 18:27:33 +05:30
ruthra kumar
bc53365620 Merge pull request #46159 from frappe/mergify/bp/version-15-hotfix/pr-46156
fix: no permission to get project settings in sales invoice (backport #46156)
2025-02-26 14:34:36 +05:30
venkat102
b8281c34e2 fix: no permission to get project settings in sales invoice
(cherry picked from commit 221f1468cb)
2025-02-26 08:22:24 +00:00
rohitwaghchaure
81ff16248e Merge pull request #46143 from frappe/mergify/bp/version-15-hotfix/pr-46097
fix: valuation rate for batch (backport #46097)
2025-02-26 10:16:10 +05:30
ruthra kumar
8bb085a055 Merge pull request #46142 from frappe/mergify/bp/version-15-hotfix/pr-45908
fix(projects settings): add checkbox to auto fetch timesheet in sales invoice (backport #45908)
2025-02-26 06:23:58 +05:30
Rohit Waghchaure
c72dab49f4 fix: valuation rate for batch
(cherry picked from commit b88305a95f)
2025-02-25 17:45:05 +00:00
ruthra kumar
feec16b682 chore: resolve conflicts 2025-02-25 22:53:14 +05:30
venkat102
feb64cb9b5 fix: enable fetch_timesheet_in_sales_invoice in test
(cherry picked from commit 5880f1d5c6)

# Conflicts:
#	erpnext/projects/doctype/timesheet/test_timesheet.py
2025-02-25 16:56:57 +00:00
venkat102
97d3e8648b fix: check value as int
(cherry picked from commit 43b13b91be)
2025-02-25 16:56:56 +00:00
venkat102
b1095bb91b fix(sales invoice): check fetch_timesheet_in_sales_invoice enabled before fetching the timesheet
(cherry picked from commit 914ad357fd)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
2025-02-25 16:56:56 +00:00
venkat102
b3c1df8561 fix(project settings): add checkbox to auto fetch timesheet in sales invoice
(cherry picked from commit 876082ea2f)

# Conflicts:
#	erpnext/projects/doctype/projects_settings/projects_settings.json
2025-02-25 16:56:56 +00:00
Ninad1306
2221bf1cba fix(report): filter sales / purchase orders based on date filters
(cherry picked from commit 936d7d4342)
2025-02-25 13:07:54 +00:00
Ninad1306
8799af9747 fix(report): allow Closed sales orders to be visible
(cherry picked from commit 2394e76e7d)

# Conflicts:
#	erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
2025-02-25 13:07:54 +00:00
Ninad1306
20c4487853 fix(report): allow Closed purchase orders to be visible
(cherry picked from commit 3b2879d3a1)

# Conflicts:
#	erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
2025-02-25 13:07:53 +00:00
0xD0M1M0
f609012f02 fix: discount accounting for v15 2025-02-21 22:26:39 +01:00
Ravibharathi
171df3aba5 feat: add total weight in shipment (#46049)
Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit 1ec182430d)

# Conflicts:
#	erpnext/stock/doctype/shipment/shipment.json
2025-02-21 14:16:46 +00:00
mergify[bot]
0ae2d61974 fix: pos opening entry dialog not saving on change data (backport #46066) (#46067)
fix: pos opening entry dialog not saving on change data (#46066)

(cherry picked from commit 8e6959dfad)

Co-authored-by: Diptanil Saha <diptanil@frappe.io>
2025-02-21 17:53:59 +05:30
rohitwaghchaure
b00fa1dfee Merge pull request #46041 from frappe/mergify/bp/version-15-hotfix/pr-46037
fix: inventory dimension for maintenance visit (backport #46037)
2025-02-20 13:06:33 +05:30
Rohit Waghchaure
ec3b281a3b fix: inventory dimension for maintence visit
(cherry picked from commit cd4ba69262)
2025-02-20 06:13:19 +00:00
rohitwaghchaure
9318e4f0e8 Merge pull request #46028 from frappe/mergify/bp/version-15-hotfix/pr-46021
fix: incorrect stock value difference for adjustment entry (backport #46021)
2025-02-19 17:24:42 +05:30
Rohit Waghchaure
96d44e362d fix: incorrect stock value difference for adjustment entry
(cherry picked from commit df83e427a3)
2025-02-19 11:11:15 +00:00
Mihir Kandoi
6157fed71c fix: source warehouse not fetched in bom creator 2025-02-19 15:54:31 +05:30
186 changed files with 8058 additions and 866 deletions

4
.github/release.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
changelog:
exclude:
labels:
- skip-release-notes

View File

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

View File

@@ -4,7 +4,7 @@
import frappe
from frappe import _, throw
from frappe.utils import cint, cstr
from frappe.utils import add_to_date, cint, cstr, pretty_date
from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of
import erpnext
@@ -481,6 +481,7 @@ def get_account_autoname(account_number, account_name, company):
@frappe.whitelist()
def update_account_number(name, account_name, account_number=None, from_descendant=False):
_ensure_idle_system()
account = frappe.get_cached_doc("Account", name)
if not account:
return
@@ -501,7 +502,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
"name",
)
if old_name:
if old_name and not from_descendant:
# same account in parent company exists
allow_child_account_creation = _("Allow Account Creation Against Child Company")
@@ -542,6 +543,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
@frappe.whitelist()
def merge_account(old, new):
_ensure_idle_system()
# Validate properties before merging
new_account = frappe.get_cached_doc("Account", new)
old_account = frappe.get_cached_doc("Account", old)
@@ -595,3 +597,31 @@ def sync_update_account_number_in_child(
for d in frappe.db.get_values("Account", filters=filters, fieldname=["company", "name"], as_dict=True):
update_account_number(d["name"], account_name, account_number, from_descendant=True)
def _ensure_idle_system():
# Don't allow renaming if accounting entries are actively being updated, there are two main reasons:
# 1. Correctness: It's next to impossible to ensure that renamed account is not being used *right now*.
# 2. Performance: Renaming requires locking out many tables entirely and severely degrades performance.
if frappe.flags.in_test:
return
last_gl_update = None
try:
# We also lock inserts to GL entry table with for_update here.
last_gl_update = frappe.db.get_value("GL Entry", {}, "modified", for_update=True, wait=False)
except frappe.QueryTimeoutError:
# wait=False fails immediately if there's an active transaction.
last_gl_update = add_to_date(None, seconds=-1)
if not last_gl_update:
return
if last_gl_update > add_to_date(None, minutes=-5):
frappe.throw(
_(
"Last GL Entry update was done {}. This operation is not allowed while system is actively being used. Please wait for 5 minutes before retrying."
).format(pretty_date(last_gl_update)),
title=_("System In Use"),
)

View File

@@ -98,7 +98,7 @@
"Office Maintenance Expenses": {},
"Office Rent": {},
"Postal Expenses": {},
"Print and Stationary": {},
"Print and Stationery": {},
"Rounded Off": {
"account_type": "Round Off"
},

View File

@@ -374,9 +374,36 @@ def auto_reconcile_vouchers(
to_reference_date=None,
):
frappe.flags.auto_reconcile_vouchers = True
reconciled, partially_reconciled = set(), set()
bank_transactions = get_bank_transactions(bank_account)
if len(bank_transactions) > 10:
frappe.enqueue(
method="erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.start_auto_reconcile",
queue="long",
bank_transactions=bank_transactions,
from_date=from_date,
to_date=to_date,
filter_by_reference_date=filter_by_reference_date,
from_reference_date=from_reference_date,
to_reference_date=to_reference_date,
)
frappe.msgprint(_("Auto Reconciliation has started in the background"))
else:
start_auto_reconcile(
bank_transactions,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
def start_auto_reconcile(
bank_transactions, from_date, to_date, filter_by_reference_date, from_reference_date, to_reference_date
):
reconciled, partially_reconciled = set(), set()
for transaction in bank_transactions:
linked_payments = get_linked_payments(
transaction.name,
@@ -414,7 +441,6 @@ def auto_reconcile_vouchers(
frappe.msgprint(title=_("Auto Reconciliation"), msg=alert_message, indicator=indicator)
frappe.flags.auto_reconcile_vouchers = False
return reconciled, partially_reconciled
def get_auto_reconcile_message(partially_reconciled, reconciled):

View File

@@ -128,7 +128,7 @@ class TestCouponCode(unittest.TestCase):
item_code="_Test Tesla Car",
rate=5000,
qty=1,
do_not_submit=True,
do_not_save=True,
)
self.assertEqual(so.items[0].rate, 5000)

View File

@@ -279,7 +279,8 @@
{
"fieldname": "transaction_exchange_rate",
"fieldtype": "Float",
"label": "Transaction Exchange Rate"
"label": "Transaction Exchange Rate",
"precision": "9"
},
{
"fieldname": "debit_in_transaction_currency",
@@ -357,7 +358,7 @@
"idx": 1,
"in_create": 1,
"links": [],
"modified": "2024-08-22 13:03:39.997475",
"modified": "2025-02-21 14:36:49.431166",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",

View File

@@ -141,6 +141,7 @@ class JournalEntry(AccountsController):
self.validate_empty_accounts_table()
self.validate_inter_company_accounts()
self.validate_depr_entry_voucher_type()
self.validate_company_in_accounting_dimension()
self.validate_advance_accounts()
if self.docstatus == 0:
@@ -1059,14 +1060,15 @@ class JournalEntry(AccountsController):
gl_map = []
company_currency = erpnext.get_company_currency(self.company)
self.transaction_currency = company_currency
self.transaction_exchange_rate = 1
if self.multi_currency:
for row in self.get("accounts"):
if row.account_currency != company_currency:
self.currency = row.account_currency
self.conversion_rate = row.exchange_rate
# Journal assumes the first foreign currency as transaction currency
self.transaction_currency = row.account_currency
self.transaction_exchange_rate = row.exchange_rate
break
else:
self.currency = company_currency
for d in self.get("accounts"):
if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
@@ -1091,6 +1093,18 @@ class JournalEntry(AccountsController):
"credit_in_account_currency": flt(
d.credit_in_account_currency, d.precision("credit_in_account_currency")
),
"transaction_currency": self.transaction_currency,
"transaction_exchange_rate": self.transaction_exchange_rate,
"debit_in_transaction_currency": flt(
d.debit_in_account_currency, d.precision("debit_in_account_currency")
)
if self.transaction_currency == d.account_currency
else flt(d.debit, d.precision("debit")) / self.transaction_exchange_rate,
"credit_in_transaction_currency": flt(
d.credit_in_account_currency, d.precision("credit_in_account_currency")
)
if self.transaction_currency == d.account_currency
else flt(d.credit, d.precision("credit")) / self.transaction_exchange_rate,
"against_voucher_type": d.reference_type,
"against_voucher": d.reference_name,
"remarks": remarks,

View File

@@ -575,7 +575,7 @@ class TestJournalEntry(unittest.TestCase):
order_by="account",
)
expected = [
{"account": "_Test Bank - _TC", "transaction_exchange_rate": 1.0},
{"account": "_Test Bank - _TC", "transaction_exchange_rate": 85.0},
{"account": "_Test Receivable USD - _TC", "transaction_exchange_rate": 85.0},
]
self.assertEqual(expected, actual)
@@ -591,13 +591,14 @@ def make_journal_entry(
save=True,
submit=False,
project=None,
company=None,
):
if not cost_center:
cost_center = "_Test Cost Center - _TC"
jv = frappe.new_doc("Journal Entry")
jv.posting_date = posting_date or nowdate()
jv.company = "_Test Company"
jv.company = company or "_Test Company"
jv.user_remark = "test"
jv.multi_currency = 1
jv.set(

View File

@@ -258,6 +258,10 @@ frappe.ui.form.on("Payment Entry", {
frappe.flags.allocate_payment_amount = true;
},
validate: async function (frm) {
await frm.events.set_exchange_gain_loss_deduction(frm);
},
validate_company: (frm) => {
if (!frm.doc.company) {
frappe.throw({ message: __("Please select a Company first."), title: __("Mandatory") });
@@ -1893,8 +1897,6 @@ function prompt_for_missing_account(frm, account) {
(values) => resolve(values?.[account]),
__("Please Specify Account")
);
dialog.on_hide = () => resolve("");
});
}

View File

@@ -1217,15 +1217,22 @@ class PaymentEntry(AccountsController):
self.set("remarks", "\n".join(remarks))
def set_transaction_currency_and_rate(self):
company_currency = erpnext.get_company_currency(self.company)
self.transaction_currency = company_currency
self.transaction_exchange_rate = 1
if self.paid_from_account_currency != company_currency:
self.transaction_currency = self.paid_from_account_currency
self.transaction_exchange_rate = self.source_exchange_rate
elif self.paid_to_account_currency != company_currency:
self.transaction_currency = self.paid_to_account_currency
self.transaction_exchange_rate = self.target_exchange_rate
def build_gl_map(self):
if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"):
self.setup_party_account_field()
company_currency = erpnext.get_company_currency(self.company)
if self.paid_from_account_currency != company_currency:
self.currency = self.paid_from_account_currency
elif self.paid_to_account_currency != company_currency:
self.currency = self.paid_to_account_currency
self.set_transaction_currency_and_rate()
gl_entries = []
self.add_party_gl_entries(gl_entries)
@@ -1304,6 +1311,9 @@ class PaymentEntry(AccountsController):
"cost_center": cost_center,
dr_or_cr + "_in_account_currency": d.allocated_amount,
dr_or_cr: allocated_amount_in_company_currency,
dr_or_cr + "_in_transaction_currency": d.allocated_amount
if self.transaction_currency == self.party_account_currency
else allocated_amount_in_company_currency / self.transaction_exchange_rate,
},
item=self,
)
@@ -1348,6 +1358,9 @@ class PaymentEntry(AccountsController):
"account_currency": self.party_account_currency,
"cost_center": self.cost_center,
dr_or_cr + "_in_account_currency": self.unallocated_amount,
dr_or_cr + "_in_transaction_currency": self.unallocated_amount
if self.party_account_currency == self.transaction_currency
else base_unallocated_amount / self.transaction_exchange_rate,
dr_or_cr: base_unallocated_amount,
},
item=self,
@@ -1365,6 +1378,7 @@ class PaymentEntry(AccountsController):
def make_advance_gl_entries(
self, entry: object | dict = None, cancel: bool = 0, update_outstanding: str = "Yes"
):
self.set_transaction_currency_and_rate()
gl_entries = []
self.add_advance_gl_entries(gl_entries, entry)
@@ -1444,9 +1458,16 @@ class PaymentEntry(AccountsController):
frappe.db.set_value("Payment Entry Reference", invoice.name, "reconcile_effect_on", posting_date)
dr_or_cr, account = self.get_dr_and_account_for_advances(invoice)
base_allocated_amount = self.calculate_base_allocated_amount_for_reference(invoice)
args_dict["account"] = account
args_dict[dr_or_cr] = self.calculate_base_allocated_amount_for_reference(invoice)
args_dict[dr_or_cr] = base_allocated_amount
args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount
args_dict[dr_or_cr + "_in_transaction_currency"] = (
invoice.allocated_amount
if self.party_account_currency == self.transaction_currency
else base_allocated_amount / self.transaction_exchange_rate
)
args_dict.update(
{
"against_voucher_type": invoice.reference_doctype,
@@ -1464,8 +1485,13 @@ class PaymentEntry(AccountsController):
args_dict[dr_or_cr + "_in_account_currency"] = 0
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
args_dict["account"] = self.party_account
args_dict[dr_or_cr] = self.calculate_base_allocated_amount_for_reference(invoice)
args_dict[dr_or_cr] = base_allocated_amount
args_dict[dr_or_cr + "_in_account_currency"] = invoice.allocated_amount
args_dict[dr_or_cr + "_in_transaction_currency"] = (
invoice.allocated_amount
if self.party_account_currency == self.transaction_currency
else base_allocated_amount / self.transaction_exchange_rate
)
args_dict.update(
{
"against_voucher_type": "Payment Entry",
@@ -1487,6 +1513,9 @@ class PaymentEntry(AccountsController):
"account_currency": self.paid_from_account_currency,
"against": self.party if self.payment_type == "Pay" else self.paid_to,
"credit_in_account_currency": self.paid_amount,
"credit_in_transaction_currency": self.paid_amount
if self.paid_from_account_currency == self.transaction_currency
else self.base_paid_amount / self.transaction_exchange_rate,
"credit": self.base_paid_amount,
"cost_center": self.cost_center,
"post_net_value": True,
@@ -1502,6 +1531,9 @@ class PaymentEntry(AccountsController):
"account_currency": self.paid_to_account_currency,
"against": self.party if self.payment_type == "Receive" else self.paid_from,
"debit_in_account_currency": self.received_amount,
"debit_in_transaction_currency": self.received_amount
if self.paid_to_account_currency == self.transaction_currency
else self.base_received_amount / self.transaction_exchange_rate,
"debit": self.base_received_amount,
"cost_center": self.cost_center,
},
@@ -1537,6 +1569,8 @@ class PaymentEntry(AccountsController):
dr_or_cr + "_in_account_currency": base_tax_amount
if account_currency == self.company_currency
else d.tax_amount,
dr_or_cr + "_in_transaction_currency": base_tax_amount
/ self.transaction_exchange_rate,
"cost_center": d.cost_center,
"post_net_value": True,
},
@@ -1562,6 +1596,8 @@ class PaymentEntry(AccountsController):
rev_dr_or_cr + "_in_account_currency": base_tax_amount
if account_currency == self.company_currency
else d.tax_amount,
rev_dr_or_cr + "_in_transaction_currency": base_tax_amount
/ self.transaction_exchange_rate,
"cost_center": self.cost_center,
"post_net_value": True,
},
@@ -1584,6 +1620,7 @@ class PaymentEntry(AccountsController):
"account_currency": account_currency,
"against": self.party or self.paid_from,
"debit_in_account_currency": d.amount,
"debit_in_transaction_currency": d.amount / self.transaction_exchange_rate,
"debit": d.amount,
"cost_center": d.cost_center,
},
@@ -1843,7 +1880,7 @@ class PaymentEntry(AccountsController):
allocated_positive_outstanding = paid_amount + allocated_negative_outstanding
elif self.party_type in ("Supplier", "Employee"):
elif self.party_type in ("Supplier", "Customer"):
if paid_amount > total_negative_outstanding:
if total_negative_outstanding == 0:
frappe.msgprint(
@@ -2854,7 +2891,9 @@ def get_payment_entry(
pe.paid_amount = paid_amount
pe.received_amount = received_amount
pe.letter_head = doc.get("letter_head")
pe.bank_account = frappe.db.get_value("Bank Account", {"is_company_account": 1, "is_default": 1}, "name")
pe.bank_account = frappe.db.get_value(
"Bank Account", {"is_company_account": 1, "is_default": 1, "company": doc.company}, "name"
)
if dt in ["Purchase Order", "Sales Order", "Sales Invoice", "Purchase Invoice"]:
pe.project = doc.get("project") or reduce(
@@ -3337,13 +3376,14 @@ def add_income_discount_loss(pe, doc, total_discount_percent) -> float:
"""Add loss on income discount in base currency."""
precision = doc.precision("total")
base_loss_on_income = doc.get("base_total") * (total_discount_percent / 100)
positive_negative = -1 if pe.payment_type == "Pay" else 1
pe.append(
"deductions",
{
"account": frappe.get_cached_value("Company", pe.company, "default_discount_account"),
"cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": flt(base_loss_on_income, precision),
"amount": flt(base_loss_on_income, precision) * positive_negative,
},
)
@@ -3355,6 +3395,7 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float:
tax_discount_loss = {}
base_total_tax_loss = 0
precision = doc.precision("tax_amount_after_discount_amount", "taxes")
positive_negative = -1 if pe.payment_type == "Pay" else 1
# The same account head could be used more than once
for tax in doc.get("taxes", []):
@@ -3377,7 +3418,7 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float:
"account": account,
"cost_center": pe.cost_center
or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": flt(loss, precision),
"amount": flt(loss, precision) * positive_negative,
},
)

View File

@@ -282,6 +282,48 @@ class TestPaymentEntry(FrappeTestCase):
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
def test_payment_entry_against_payment_terms_with_discount_on_pi(self):
pi = make_purchase_invoice(do_not_save=1)
create_payment_terms_template_with_discount()
pi.payment_terms_template = "Test Discount Template"
frappe.db.set_value("Company", pi.company, "default_discount_account", "Write Off - _TC")
pi.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account Service Tax - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Service Tax",
"rate": 18,
},
)
pi.save()
pi.submit()
frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 1)
pe_with_tax_loss = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Cash - _TC")
self.assertEqual(pe_with_tax_loss.references[0].payment_term, "30 Credit Days with 10% Discount")
self.assertEqual(pe_with_tax_loss.payment_type, "Pay")
self.assertEqual(pe_with_tax_loss.references[0].allocated_amount, 295.0)
self.assertEqual(pe_with_tax_loss.paid_amount, 265.5)
self.assertEqual(pe_with_tax_loss.difference_amount, 0)
self.assertEqual(pe_with_tax_loss.deductions[0].amount, -25.0) # Loss on Income
self.assertEqual(pe_with_tax_loss.deductions[1].amount, -4.5) # Loss on Tax
self.assertEqual(pe_with_tax_loss.deductions[1].account, "_Test Account Service Tax - _TC")
frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 0)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Cash - _TC")
self.assertEqual(pe.references[0].payment_term, "30 Credit Days with 10% Discount")
self.assertEqual(pe.payment_type, "Pay")
self.assertEqual(pe.references[0].allocated_amount, 295.0)
self.assertEqual(pe.paid_amount, 265.5)
self.assertEqual(pe.deductions[0].amount, -29.5)
self.assertEqual(pe.difference_amount, 0)
def test_payment_entry_against_payment_terms_with_discount(self):
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
create_payment_terms_template_with_discount()

View File

@@ -768,29 +768,39 @@ def get_existing_payment_request_amount(ref_dt, ref_dn, statuses: list | None =
def get_existing_paid_amount(doctype, name):
PL = frappe.qb.DocType("Payment Ledger Entry")
PLE = frappe.qb.DocType("Payment Ledger Entry")
PER = frappe.qb.DocType("Payment Entry Reference")
query = (
frappe.qb.from_(PL)
frappe.qb.from_(PLE)
.left_join(PER)
.on(
(PL.against_voucher_type == PER.reference_doctype)
& (PL.against_voucher_no == PER.reference_name)
& (PL.voucher_type == PER.parenttype)
& (PL.voucher_no == PER.parent)
(PLE.against_voucher_type == PER.reference_doctype)
& (PLE.against_voucher_no == PER.reference_name)
& (PLE.voucher_type == PER.parenttype)
& (PLE.voucher_no == PER.parent)
)
.select(
Abs(Sum(PLE.amount)).as_("total_amount"),
Abs(Sum(frappe.qb.terms.Case().when(PER.payment_request.isnotnull(), PLE.amount).else_(0))).as_(
"request_paid_amount"
),
)
.where(
(PLE.voucher_type.isin([doctype, "Journal Entry", "Payment Entry"]))
& (PLE.against_voucher_type == doctype)
& (PLE.against_voucher_no == name)
& (PLE.delinked == 0)
& (PLE.docstatus == 1)
& (PLE.amount < 0)
)
.select(Abs(Sum(PL.amount)).as_("total_paid_amount"))
.where(PL.against_voucher_type.eq(doctype))
.where(PL.against_voucher_no.eq(name))
.where(PL.amount < 0)
.where(PL.delinked == 0)
.where(PER.docstatus == 1)
.where(PER.payment_request.isnull())
)
response = query.run()
return response[0][0] if response[0] else 0
result = query.run()
ledger_amount = flt(result[0][0]) if result else 0
request_paid_amount = flt(result[0][1]) if result else 0
return ledger_amount - request_paid_amount
def get_gateway_details(args): # nosemgrep

View File

@@ -581,6 +581,34 @@ class TestPaymentRequest(FrappeTestCase):
pi.load_from_db()
self.assertEqual(pr_2.grand_total, pi.outstanding_amount)
def test_consider_journal_entry_and_return_invoice(self):
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
si = create_sales_invoice(currency="INR", qty=5, rate=500)
je = make_journal_entry("_Test Cash - _TC", "Debtors - _TC", 500, save=False)
je.accounts[1].party_type = "Customer"
je.accounts[1].party = si.customer
je.accounts[1].reference_type = "Sales Invoice"
je.accounts[1].reference_name = si.name
je.accounts[1].credit_in_account_currency = 500
je.submit()
pe = get_payment_entry("Sales Invoice", si.name)
pe.paid_amount = 500
pe.references[0].allocated_amount = 500
pe.save()
pe.submit()
cr_note = create_sales_invoice(qty=-1, rate=500, is_return=1, return_against=si.name, do_not_save=1)
cr_note.update_outstanding_for_self = 0
cr_note.save()
cr_note.submit()
si.load_from_db()
pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1)
self.assertEqual(pr.grand_total, si.outstanding_amount)
def test_partial_paid_invoice_with_submitted_payment_entry(self):
pi = make_purchase_invoice(currency="INR", qty=1, rate=5000)

View File

@@ -139,7 +139,7 @@ class PeriodClosingVoucher(AccountsController):
self.cancel_gl_entries()
def make_gl_entries(self):
if self.get_gle_count_in_selected_period() > 5000:
if frappe.db.estimate_count("GL Entry") > 100_000:
frappe.enqueue(
process_gl_and_closing_entries,
doc=self,
@@ -154,16 +154,6 @@ class PeriodClosingVoucher(AccountsController):
else:
process_gl_and_closing_entries(self)
def get_gle_count_in_selected_period(self):
return frappe.db.count(
"GL Entry",
{
"posting_date": ["between", [self.period_start_date, self.period_end_date]],
"company": self.company,
"is_cancelled": 0,
},
)
def get_pcv_gl_entries(self):
self.pl_accounts_reverse_gle = []
self.closing_account_gle = []

View File

@@ -27,6 +27,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center,
company=company,
save=False,
)
jv1.company = company
@@ -39,6 +40,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cost of Goods Sold - TPC",
account2="Cash - TPC",
cost_center=cost_center,
company=company,
save=False,
)
jv2.company = company
@@ -156,6 +158,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
amount=400,
cost_center=cost_center,
posting_date="2021-03-15",
company=company,
)
jv.company = company
jv.finance_book = create_finance_book().name
@@ -198,6 +201,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center,
company=company,
save=False,
)
jv1.company = company
@@ -220,6 +224,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center1,
company=company,
save=False,
)
jv1.company = company
@@ -232,6 +237,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center2,
company=company,
save=False,
)
jv2.company = company
@@ -261,6 +267,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center2,
company=company,
save=False,
)

View File

@@ -1623,6 +1623,5 @@
"states": [],
"timeline_field": "customer",
"title_field": "title",
"track_changes": 1,
"track_seen": 1
}
"track_changes": 1
}

View File

@@ -8,6 +8,7 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.model.mapper import map_child_doc, map_doc
from frappe.query_builder import DocType
from frappe.utils import cint, flt, get_time, getdate, nowdate, nowtime
from frappe.utils.background_jobs import enqueue, is_job_enqueued
from frappe.utils.scheduler import is_scheduler_inactive
@@ -118,17 +119,18 @@ class POSInvoiceMergeLog(Document):
returns = [d for d in pos_invoice_docs if d.get("is_return") == 1]
sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
sales_invoice, credit_note = "", ""
sales_invoice, credit_notes = "", {}
sales_invoice_doc = None
if sales:
sales_invoice_doc = self.process_merging_into_sales_invoice(sales)
sales_invoice = sales_invoice_doc.name
if returns:
credit_note = self.process_merging_into_credit_note(returns, sales_invoice_doc)
distinguished_returns = self.distinguish_return_pos_invoices(returns, sales_invoice_doc)
credit_notes = self.process_merging_into_credit_notes(distinguished_returns)
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_notes)
def on_cancel(self):
pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
@@ -158,34 +160,50 @@ class POSInvoiceMergeLog(Document):
return sales_invoice
def process_merging_into_credit_note(self, data, sales_invoice_doc=None):
credit_note = self.get_new_sales_invoice()
credit_note.is_return = 1
def process_merging_into_credit_notes(self, data):
credit_notes = {}
for key, value in data.items():
if not value:
continue
credit_note = self.merge_pos_invoice_into(credit_note, data)
referenes = {}
credit_note = self.get_new_sales_invoice()
credit_note.is_return = 1
if sales_invoice_doc:
credit_note.return_against = sales_invoice_doc.name
credit_note = self.merge_pos_invoice_into(credit_note, value)
credit_note.return_against = key
for d in sales_invoice_doc.items:
referenes[d.item_code] = d.name
credit_note.is_consolidated = 1
credit_note.set_posting_time = 1
credit_note.posting_date = getdate(self.posting_date)
credit_note.posting_time = get_time(self.posting_time)
# TODO: return could be against multiple sales invoice which could also have been consolidated?
# credit_note.return_against = self.consolidated_invoice
credit_note.save()
credit_note.submit()
for d in credit_note.items:
d.sales_invoice_item = referenes.get(d.item_code)
self.consolidated_credit_note = credit_note.name
credit_notes[credit_note.name] = [d.name for d in value]
credit_note.is_consolidated = 1
credit_note.set_posting_time = 1
credit_note.posting_date = getdate(self.posting_date)
credit_note.posting_time = get_time(self.posting_time)
# TODO: return could be against multiple sales invoice which could also have been consolidated?
# credit_note.return_against = self.consolidated_invoice
credit_note.save()
credit_note.submit()
return credit_notes
self.consolidated_credit_note = credit_note.name
def distinguish_return_pos_invoices(self, data, sales_invoice_doc=None):
return_invoices = {}
return credit_note.name
return_invoices[sales_invoice_doc.name if sales_invoice_doc else None] = []
for doc in data:
sales_invoices_of_return_against = frappe.db.get_value(
"POS Invoice", doc.return_against, "consolidated_invoice"
)
if sales_invoices_of_return_against:
if sales_invoices_of_return_against in return_invoices:
return_invoices[sales_invoices_of_return_against].append(doc)
else:
return_invoices[sales_invoices_of_return_against] = [doc]
else:
return_invoices[sales_invoice_doc.name if sales_invoice_doc else None].append(doc)
return return_invoices
def merge_pos_invoice_into(self, invoice, data):
items, payments, taxes = [], [], []
@@ -211,33 +229,20 @@ class POSInvoiceMergeLog(Document):
loyalty_amount_sum += doc.loyalty_amount
for item in doc.get("items"):
found = False
for i in items:
if (
i.item_code == item.item_code
and not i.serial_and_batch_bundle
and not i.serial_no
and not i.batch_no
and i.uom == item.uom
and i.net_rate == item.net_rate
and i.warehouse == item.warehouse
):
found = True
i.qty = i.qty + item.qty
i.amount = i.amount + item.net_amount
i.net_amount = i.amount
i.base_amount = i.base_amount + item.base_net_amount
i.base_net_amount = i.base_amount
if not found:
item.rate = item.net_rate
item.amount = item.net_amount
item.base_amount = item.base_net_amount
item.price_list_rate = 0
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
if item.serial_and_batch_bundle:
si_item.serial_and_batch_bundle = item.serial_and_batch_bundle
items.append(si_item)
item.rate = item.net_rate
item.amount = item.net_amount
item.base_amount = item.base_net_amount
item.price_list_rate = 0
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
si_item.pos_invoice = doc.name
si_item.pos_invoice_item = item.name
if doc.is_return:
si_item.sales_invoice_item = get_sales_invoice_item(
doc.return_against, item.pos_invoice_item
)
if item.serial_and_batch_bundle:
si_item.serial_and_batch_bundle = item.serial_and_batch_bundle
items.append(si_item)
for tax in doc.get("taxes"):
found = False
@@ -327,16 +332,16 @@ class POSInvoiceMergeLog(Document):
return sales_invoice
def update_pos_invoices(self, invoice_docs, sales_invoice="", credit_note=""):
def update_pos_invoices(self, invoice_docs, sales_invoice="", credit_notes=None):
for doc in invoice_docs:
doc.load_from_db()
doc.update(
{
"consolidated_invoice": None
if self.docstatus == 2
else (credit_note if doc.is_return else sales_invoice)
}
)
inv = sales_invoice
if doc.is_return:
for key, value in credit_notes.items():
if doc.name in value:
inv = key
break
doc.update({"consolidated_invoice": None if self.docstatus == 2 else inv})
doc.set_status(update=True)
doc.save()
@@ -628,3 +633,26 @@ def get_error_message(message) -> str:
return message["message"]
except Exception:
return str(message)
def get_sales_invoice_item(return_against_pos_invoice, pos_invoice_item):
try:
SalesInvoice = DocType("Sales Invoice")
SalesInvoiceItem = DocType("Sales Invoice Item")
query = (
frappe.qb.from_(SalesInvoice)
.from_(SalesInvoiceItem)
.select(SalesInvoiceItem.name)
.where(
(SalesInvoice.name == SalesInvoiceItem.parent)
& (SalesInvoice.is_return == 0)
& (SalesInvoiceItem.pos_invoice == return_against_pos_invoice)
& (SalesInvoiceItem.pos_invoice_item == pos_invoice_item)
)
)
result = query.run(as_dict=True)
return result[0].name if result else None
except Exception:
return None

View File

@@ -4,6 +4,7 @@
import frappe
from frappe import _, msgprint, scrub, unscrub
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
from frappe.model.document import Document
from frappe.utils import get_link_to_form, now
@@ -204,17 +205,41 @@ class POSProfile(Document):
def get_item_groups(pos_profile):
item_groups = []
pos_profile = frappe.get_cached_doc("POS Profile", pos_profile)
permitted_item_groups = get_permitted_nodes("Item Group")
if pos_profile.get("item_groups"):
# Get items based on the item groups defined in the POS profile
for data in pos_profile.get("item_groups"):
item_groups.extend(
["%s" % frappe.db.escape(d.name) for d in get_child_nodes("Item Group", data.item_group)]
[
"%s" % frappe.db.escape(d.name)
for d in get_child_nodes("Item Group", data.item_group)
if not permitted_item_groups or d.name in permitted_item_groups
]
)
if not item_groups and permitted_item_groups:
item_groups = ["%s" % frappe.db.escape(d) for d in permitted_item_groups]
return list(set(item_groups))
def get_permitted_nodes(group_type):
nodes = []
permitted_nodes = get_permitted_documents(group_type)
if not permitted_nodes:
return nodes
for node in permitted_nodes:
if frappe.db.get_value(group_type, node, "is_group"):
nodes.extend([d.name for d in get_child_nodes(group_type, node)])
else:
nodes.append(node)
return nodes
def get_child_nodes(group_type, root):
lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"])
return frappe.db.sql(

View File

@@ -454,8 +454,7 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False):
if pricing_rule.coupon_code_based == 1:
if not args.coupon_code:
return item_details
continue
coupon_code = frappe.db.get_value(
doctype="Coupon Code", filters={"pricing_rule": pricing_rule.name}, fieldname="name"
)

View File

@@ -871,6 +871,7 @@ class PurchaseInvoice(BuyingController):
self.make_payment_gl_entries(gl_entries)
self.make_write_off_gl_entry(gl_entries)
self.make_gle_for_rounding_adjustment(gl_entries)
self.set_transaction_currency_and_rate_in_gl_map(gl_entries)
return gl_entries
def check_asset_cwip_enabled(self):
@@ -916,6 +917,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"credit_in_transaction_currency": grand_total,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"project": self.project,
@@ -951,7 +953,7 @@ class PurchaseInvoice(BuyingController):
valuation_tax_accounts = [
d.account_head
for d in self.get("taxes")
if d.category in ("Valuation", "Total and Valuation")
if d.category in ("Valuation", "Valuation and Total")
and flt(d.base_tax_amount_after_discount_amount)
]
@@ -967,7 +969,6 @@ class PurchaseInvoice(BuyingController):
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account)
if item.item_code:
frappe.get_cached_value("Item", item.item_code, "asset_category")
@@ -976,6 +977,7 @@ class PurchaseInvoice(BuyingController):
and self.auto_accounting_for_stock
and (item.item_code in stock_items or item.is_fixed_asset)
):
account_currency = get_account_currency(item.expense_account)
# warehouse account
warehouse_debit_amount = self.make_stock_adjustment_entry(
gl_entries, item, voucher_wise_stock_value, account_currency
@@ -991,6 +993,7 @@ class PurchaseInvoice(BuyingController):
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": warehouse_debit_amount,
"debit_in_transaction_currency": item.net_amount,
},
warehouse_account[item.warehouse]["account_currency"],
item=item,
@@ -1011,6 +1014,7 @@ class PurchaseInvoice(BuyingController):
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": -1 * flt(credit_amount, item.precision("base_net_amount")),
"debit_in_transaction_currency": item.net_amount,
},
warehouse_account[item.from_warehouse]["account_currency"],
item=item,
@@ -1025,6 +1029,7 @@ class PurchaseInvoice(BuyingController):
"account": item.expense_account,
"against": self.supplier,
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
"debit_in_transaction_currency": item.net_amount,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
"project": item.project,
@@ -1042,6 +1047,10 @@ class PurchaseInvoice(BuyingController):
"account": item.expense_account,
"against": self.supplier,
"debit": warehouse_debit_amount,
"debit_in_transaction_currency": flt(
warehouse_debit_amount / self.conversion_rate,
item.precision("net_amount"),
),
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
"project": item.project or self.project,
@@ -1054,7 +1063,9 @@ class PurchaseInvoice(BuyingController):
# Amount added through landed-cost-voucher
if landed_cost_entries:
if (item.item_code, item.name) in landed_cost_entries:
for account, amount in landed_cost_entries[(item.item_code, item.name)].items():
for account, base_amount in landed_cost_entries[
(item.item_code, item.name)
].items():
gl_entries.append(
self.get_gl_dict(
{
@@ -1062,8 +1073,9 @@ class PurchaseInvoice(BuyingController):
"against": item.expense_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(amount["base_amount"]),
"credit_in_account_currency": flt(amount["amount"]),
"credit": flt(base_amount["base_amount"]),
"credit_in_account_currency": flt(base_amount["amount"]),
"credit_in_transaction_currency": item.net_amount,
"project": item.project or self.project,
},
item=item,
@@ -1086,6 +1098,7 @@ class PurchaseInvoice(BuyingController):
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.rm_supp_cost),
"credit_in_transaction_currency": item.net_amount,
},
warehouse_account[self.supplier_warehouse]["account_currency"],
item=item,
@@ -1099,7 +1112,8 @@ class PurchaseInvoice(BuyingController):
else item.deferred_expense_account
)
dummy, amount = self.get_amount_and_base_amount(item, None)
account_currency = get_account_currency(expense_account)
amount, base_amount = self.get_amount_and_base_amount(item, None)
if provisional_accounting_for_non_stock_items:
self.make_provisional_gl_entry(gl_entries, item)
@@ -1110,7 +1124,8 @@ class PurchaseInvoice(BuyingController):
{
"account": expense_account,
"against": self.supplier,
"debit": amount,
"debit": base_amount,
"debit_in_transaction_currency": amount,
"cost_center": item.cost_center,
"project": item.project or self.project,
},
@@ -1184,6 +1199,10 @@ class PurchaseInvoice(BuyingController):
"account": stock_rbnb,
"against": self.supplier,
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
"debit_in_transaction_currency": flt(
item.item_tax_amount / self.conversion_rate,
item.precision("item_tax_amount"),
),
"remarks": self.remarks or _("Accounting Entry for Stock"),
"cost_center": self.cost_center,
"project": item.project or self.project,
@@ -1299,6 +1318,7 @@ class PurchaseInvoice(BuyingController):
"account": cost_of_goods_sold_account,
"against": item.expense_account,
"debit": stock_adjustment_amt,
"debit_in_transaction_currency": stock_adjustment_amt / self.conversion_rate,
"remarks": self.get("remarks") or _("Stock Adjustment"),
"cost_center": item.cost_center,
"project": item.project or self.project,
@@ -1310,6 +1330,38 @@ class PurchaseInvoice(BuyingController):
warehouse_debit_amount = stock_amount
elif self.is_return and self.update_stock and self.is_internal_supplier and warehouse_debit_amount:
net_rate = item.base_net_amount
if item.sales_incoming_rate: # for internal transfer
net_rate = item.qty * item.sales_incoming_rate
stock_amount = (
net_rate
+ item.item_tax_amount
+ flt(item.landed_cost_voucher_amount)
+ flt(item.get("amount_difference_with_purchase_invoice"))
)
if flt(stock_amount, net_amt_precision) != flt(warehouse_debit_amount, net_amt_precision):
cost_of_goods_sold_account = self.get_company_default("default_expense_account")
stock_adjustment_amt = stock_amount - warehouse_debit_amount
gl_entries.append(
self.get_gl_dict(
{
"account": cost_of_goods_sold_account,
"against": item.expense_account,
"debit": stock_adjustment_amt,
"debit_in_transaction_currency": stock_adjustment_amt / self.conversion_rate,
"remarks": self.get("remarks") or _("Stock Adjustment"),
"cost_center": item.cost_center,
"project": item.project or self.project,
},
account_currency,
item=item,
)
)
return warehouse_debit_amount
def make_tax_gl_entries(self, gl_entries):
@@ -1332,6 +1384,7 @@ class PurchaseInvoice(BuyingController):
dr_or_cr + "_in_account_currency": base_amount
if account_currency == self.company_currency
else amount,
dr_or_cr + "_in_transaction_currency": amount,
"cost_center": tax.cost_center,
},
account_currency,
@@ -1378,6 +1431,10 @@ class PurchaseInvoice(BuyingController):
"cost_center": tax.cost_center,
"against": self.supplier,
"credit": applicable_amount,
"credit_in_transaction_currency": flt(
applicable_amount / self.conversion_rate,
frappe.get_precision("Purchase Invoice Item", "item_tax_amount"),
),
"remarks": self.remarks or _("Accounting Entry for Stock"),
},
item=tax,
@@ -1396,6 +1453,10 @@ class PurchaseInvoice(BuyingController):
"cost_center": tax.cost_center,
"against": self.supplier,
"credit": valuation_tax[tax.name],
"credit_in_transaction_currency": flt(
valuation_tax[tax.name] / self.conversion_rate,
frappe.get_precision("Purchase Invoice Item", "item_tax_amount"),
),
"remarks": self.remarks or _("Accounting Entry for Stock"),
},
item=tax,
@@ -1411,6 +1472,7 @@ class PurchaseInvoice(BuyingController):
"account": self.unrealized_profit_loss_account,
"against": self.supplier,
"credit": flt(self.total_taxes_and_charges),
"credit_in_transaction_currency": flt(self.total_taxes_and_charges),
"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
"cost_center": self.cost_center,
},
@@ -1460,6 +1522,7 @@ class PurchaseInvoice(BuyingController):
"debit_in_account_currency": self.base_paid_amount
if self.party_account_currency == self.company_currency
else self.paid_amount,
"debit_in_transaction_currency": self.paid_amount,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
@@ -1481,6 +1544,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": self.base_paid_amount
if bank_account_currency == self.company_currency
else self.paid_amount,
"credit_in_transaction_currency": self.paid_amount,
"cost_center": self.cost_center,
},
bank_account_currency,
@@ -1505,6 +1569,7 @@ class PurchaseInvoice(BuyingController):
"debit_in_account_currency": self.base_write_off_amount
if self.party_account_currency == self.company_currency
else self.write_off_amount,
"debit_in_transaction_currency": self.write_off_amount,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
@@ -1525,6 +1590,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": self.base_write_off_amount
if write_off_account_currency == self.company_currency
else self.write_off_amount,
"credit_in_transaction_currency": self.write_off_amount,
"cost_center": self.cost_center or self.write_off_cost_center,
},
item=self,

View File

@@ -2482,6 +2482,76 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
item.reload()
self.assertEqual(item.last_purchase_rate, 0)
def test_adjust_incoming_rate_from_pi_with_multi_currency_and_partial_billing(self):
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1)
pr = make_purchase_receipt(
qty=10, rate=10, currency="USD", do_not_save=1, supplier="_Test Supplier USD"
)
pr.conversion_rate = 5300
pr.save()
pr.submit()
incoming_rate = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"incoming_rate",
)
self.assertEqual(incoming_rate, 53000) # Asserting to confirm if the default calculation is correct
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.qty = 1
pi.save()
pi.submit()
incoming_rate = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"incoming_rate",
)
# Test 1 : Incoming rate should not change as only the qty has changed and not the rate (this was not the case before)
self.assertEqual(incoming_rate, 53000)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.qty = 1
row.rate = 9
pi.save()
pi.submit()
incoming_rate = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"incoming_rate",
)
# Test 2 : Rate in new PI is lower than PR, so incoming rate should also be lower
self.assertEqual(incoming_rate, 50350)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.qty = 1
row.rate = 12
pi.save()
pi.submit()
incoming_rate = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"incoming_rate",
)
# Test 3 : Rate in new PI is higher than PR, so incoming rate should also be higher
self.assertEqual(incoming_rate, 54766.667)
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
def test_opening_invoice_rounding_adjustment_validation(self):
pi = make_purchase_invoice(do_not_save=1)
pi.items[0].rate = 99.98
@@ -2569,6 +2639,50 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
"Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice", original_value
)
def test_trx_currency_debit_credit_for_high_precision(self):
exc_rate = 0.737517516
pi = make_purchase_invoice(
currency="USD", conversion_rate=exc_rate, qty=1, rate=2000, do_not_save=True
)
pi.supplier = "_Test Supplier USD"
pi.save().submit()
expected = (
("_Test Account Cost for Goods Sold - _TC", 1475.04, 0.0, 2000.0, 0.0, "USD", exc_rate),
("_Test Payable USD - _TC", 0.0, 1475.04, 0.0, 2000.0, "USD", exc_rate),
)
actual = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pi.name},
fields=[
"account",
"debit",
"credit",
"debit_in_transaction_currency",
"credit_in_transaction_currency",
"transaction_currency",
"transaction_exchange_rate",
],
order_by="account",
as_list=1,
)
self.assertEqual(actual, expected)
def test_prevents_fully_returned_invoice_with_zero_quantity(self):
from erpnext.controllers.sales_and_purchase_return import StockOverReturnError, make_return_doc
invoice = make_purchase_invoice(qty=10)
return_doc = make_return_doc(invoice.doctype, invoice.name)
return_doc.items[0].qty = -10
return_doc.save().submit()
return_doc = make_return_doc(invoice.doctype, invoice.name)
return_doc.items[0].qty = 0
self.assertRaises(StockOverReturnError, return_doc.save)
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -461,7 +461,8 @@
"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"
"label": "Serial No",
"no_copy": 1
},
{
"depends_on": "eval:!doc.is_fixed_asset && doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
@@ -975,7 +976,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-10-28 15:06:19.246141",
"modified": "2025-03-12 16:33:12.453290",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
@@ -985,4 +986,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -897,8 +897,16 @@ frappe.ui.form.on("Sales Invoice", {
project: function (frm) {
if (frm.doc.project) {
frm.events.add_timesheet_data(frm, {
project: frm.doc.project,
frappe.call({
method: "is_auto_fetch_timesheet_enabled",
doc: frm.doc,
callback: function (r) {
if (cint(r.message)) {
frm.events.add_timesheet_data(frm, {
project: frm.doc.project,
});
}
},
});
}
},
@@ -914,9 +922,25 @@ frappe.ui.form.on("Sales Invoice", {
}
const timesheets = await frm.events.get_timesheet_data(frm, kwargs);
if (kwargs.item_code) {
frm.events.add_timesheet_item(frm, kwargs.item_code, timesheets);
}
return frm.events.set_timesheet_data(frm, timesheets);
},
add_timesheet_item: function (frm, item_code, timesheets) {
const row = frm.add_child("items");
frappe.model.set_value(row.doctype, row.name, "item_code", item_code);
frappe.model.set_value(
row.doctype,
row.name,
"qty",
timesheets.reduce((a, b) => a + (b["billing_hours"] || 0.0), 0.0)
);
},
async get_timesheet_data(frm, kwargs) {
return frappe
.call({
@@ -1014,6 +1038,22 @@ frappe.ui.form.on("Sales Invoice", {
fieldtype: "Date",
reqd: 1,
},
{
label: __("Item Code"),
fieldname: "item_code",
fieldtype: "Link",
options: "Item",
get_query: () => {
return {
query: "erpnext.controllers.queries.item_query",
filters: {
is_sales_item: 1,
customer: frm.doc.customer,
has_variants: 0,
},
};
},
},
{
fieldtype: "Column Break",
fieldname: "col_break_1",
@@ -1038,6 +1078,7 @@ frappe.ui.form.on("Sales Invoice", {
from_time: data.from_time,
to_time: data.to_time,
project: data.project,
item_code: data.item_code,
});
d.hide();
},

View File

@@ -2177,6 +2177,7 @@
"print_hide": 1
}
],
"grid_page_length": 50,
"icon": "fa fa-file-text",
"idx": 181,
"is_submittable": 1,
@@ -2187,7 +2188,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2025-02-06 15:59:54.636202",
"modified": "2025-03-17 19:32:31.809658",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
@@ -2233,6 +2234,7 @@
}
],
"quick_entry": 1,
"row_format": "Dynamic",
"search_fields": "posting_date, due_date, customer, base_grand_total, outstanding_amount",
"show_name_in_global_search": 1,
"sort_field": "modified",
@@ -2242,4 +2244,4 @@
"title_field": "title",
"track_changes": 1,
"track_seen": 1
}
}

View File

@@ -678,7 +678,13 @@ class SalesInvoice(SellingController):
"Account", self.debit_to, "account_currency", cache=True
)
if not self.due_date and self.customer:
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
self.due_date = get_due_date(
self.posting_date,
"Customer",
self.customer,
self.company,
template_name=self.payment_terms_template,
)
super().set_missing_values(for_validate)
@@ -1090,11 +1096,15 @@ class SalesInvoice(SellingController):
timesheet.billing_amount = ts_doc.total_billable_amount
def update_timesheet_billing_for_project(self):
if not self.timesheets and self.project:
if not self.timesheets and self.project and self.is_auto_fetch_timesheet_enabled():
self.add_timesheet_data()
else:
self.calculate_billing_amount_for_timesheet()
@frappe.whitelist()
def is_auto_fetch_timesheet_enabled(self):
return frappe.db.get_single_value("Projects Settings", "fetch_timesheet_in_sales_invoice")
@frappe.whitelist()
def add_timesheet_data(self):
self.set("timesheets", [])
@@ -1237,6 +1247,7 @@ class SalesInvoice(SellingController):
self.make_write_off_gl_entry(gl_entries)
self.make_gle_for_rounding_adjustment(gl_entries)
self.set_transaction_currency_and_rate_in_gl_map(gl_entries)
return gl_entries
def make_customer_gl_entry(self, gl_entries):
@@ -1270,6 +1281,7 @@ class SalesInvoice(SellingController):
"debit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"debit_in_transaction_currency": grand_total,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
@@ -1301,6 +1313,9 @@ class SalesInvoice(SellingController):
if account_currency == self.company_currency
else flt(amount, tax.precision("tax_amount_after_discount_amount"))
),
"credit_in_transaction_currency": flt(
amount, tax.precision("tax_amount_after_discount_amount")
),
"cost_center": tax.cost_center,
},
account_currency,
@@ -1318,6 +1333,7 @@ class SalesInvoice(SellingController):
"against": self.customer,
"debit": flt(self.total_taxes_and_charges),
"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
"debit_in_transaction_currency": flt(self.total_taxes_and_charges),
"cost_center": self.cost_center,
},
account_currency,
@@ -1416,6 +1432,7 @@ class SalesInvoice(SellingController):
if account_currency == self.company_currency
else flt(amount, item.precision("net_amount"))
),
"credit_in_transaction_currency": flt(amount, item.precision("net_amount")),
"cost_center": item.cost_center,
"project": item.project or self.project,
},
@@ -1467,6 +1484,7 @@ class SalesInvoice(SellingController):
+ cstr(self.loyalty_redemption_account)
+ " for the Loyalty Program",
"credit": self.loyalty_amount,
"credit_in_transaction_currency": self.loyalty_amount,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
@@ -1481,6 +1499,7 @@ class SalesInvoice(SellingController):
"cost_center": self.cost_center or self.loyalty_redemption_cost_center,
"against": self.customer,
"debit": self.loyalty_amount,
"debit_in_transaction_currency": self.loyalty_amount,
"remark": "Loyalty Points redeemed by the customer",
},
item=self,
@@ -1514,6 +1533,7 @@ class SalesInvoice(SellingController):
"credit_in_account_currency": payment_mode.base_amount
if self.party_account_currency == self.company_currency
else payment_mode.amount,
"credit_in_transaction_currency": payment_mode.amount,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
@@ -1533,6 +1553,7 @@ class SalesInvoice(SellingController):
"debit_in_account_currency": payment_mode.base_amount
if payment_mode_account_currency == self.company_currency
else payment_mode.amount,
"debit_in_transaction_currency": payment_mode.amount,
"cost_center": self.cost_center,
},
payment_mode_account_currency,
@@ -1557,6 +1578,7 @@ class SalesInvoice(SellingController):
"debit_in_account_currency": flt(self.base_change_amount)
if self.party_account_currency == self.company_currency
else flt(self.change_amount),
"debit_in_transaction_currency": flt(self.change_amount),
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
@@ -1575,6 +1597,7 @@ class SalesInvoice(SellingController):
"account": self.account_for_change_amount,
"against": self.customer,
"credit": self.base_change_amount,
"credit_in_transaction_currency": self.change_amount,
"cost_center": self.cost_center,
},
item=self,
@@ -1606,6 +1629,9 @@ class SalesInvoice(SellingController):
if self.party_account_currency == self.company_currency
else flt(self.write_off_amount, self.precision("write_off_amount"))
),
"credit_in_transaction_currency": flt(
self.write_off_amount, self.precision("write_off_amount")
),
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
@@ -1626,6 +1652,9 @@ class SalesInvoice(SellingController):
if write_off_account_currency == self.company_currency
else flt(self.write_off_amount, self.precision("write_off_amount"))
),
"debit_in_transaction_currency": flt(
self.write_off_amount, self.precision("write_off_amount")
),
"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center,
},
write_off_account_currency,
@@ -1670,6 +1699,9 @@ class SalesInvoice(SellingController):
"credit_in_account_currency": flt(
self.rounding_adjustment, self.precision("rounding_adjustment")
),
"credit_in_transaction_currency": flt(
self.rounding_adjustment, self.precision("rounding_adjustment")
),
"credit": flt(
self.base_rounding_adjustment, self.precision("base_rounding_adjustment")
),
@@ -1933,13 +1965,16 @@ def is_overdue(doc, total):
"base_payment_amount" if doc.party_account_currency != doc.currency else "payment_amount"
)
payable_amount = sum(
payment.get(payment_amount_field)
for payment in doc.payment_schedule
if getdate(payment.due_date) < today
payable_amount = flt(
sum(
payment.get(payment_amount_field)
for payment in doc.payment_schedule
if getdate(payment.due_date) < today
),
doc.precision("outstanding_amount"),
)
return (total - outstanding_amount) < payable_amount
return flt(total - outstanding_amount, doc.precision("outstanding_amount")) < payable_amount
def get_discounting_status(sales_invoice):

View File

@@ -4246,6 +4246,31 @@ class TestSalesInvoice(FrappeTestCase):
doc = frappe.get_doc("Project", project.name)
self.assertEqual(doc.total_billed_amount, si.grand_total)
def test_total_billed_amount_with_different_projects(self):
# This test case is for checking the scenario where project is set at document level and for **some** child items only, not all
from copy import copy
si = create_sales_invoice(do_not_submit=True)
project = frappe.new_doc("Project")
project.company = "_Test Company"
project.project_name = "Test Total Billed Amount"
project.save()
si.project = project.name
si.items.append(copy(si.items[0]))
si.items.append(copy(si.items[0]))
si.items[0].project = project.name
si.items[1].project = project.name
# Not setting project on last item
si.items[1].insert()
si.items[2].insert()
si.submit()
project.reload()
self.assertIsNone(si.items[2].project)
self.assertEqual(project.total_billed_amount, 300)
def test_pos_returns_with_party_account_currency(self):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
@@ -4270,6 +4295,20 @@ class TestSalesInvoice(FrappeTestCase):
pos_return = make_sales_return(pos.name)
self.assertEqual(abs(pos_return.payments[0].amount), pos.payments[0].amount)
def test_prevents_fully_returned_invoice_with_zero_quantity(self):
from erpnext.controllers.sales_and_purchase_return import StockOverReturnError, make_return_doc
invoice = create_sales_invoice(qty=10)
return_doc = make_return_doc(invoice.doctype, invoice.name)
return_doc.items[0].qty = -10
return_doc.save().submit()
return_doc = make_return_doc(invoice.doctype, invoice.name)
return_doc.items[0].qty = 0
self.assertRaises(StockOverReturnError, return_doc.save)
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -105,6 +105,9 @@
"delivery_note",
"dn_detail",
"delivered_qty",
"column_break_vwhb",
"pos_invoice",
"pos_invoice_item",
"internal_transfer_section",
"purchase_order",
"column_break_92",
@@ -630,6 +633,7 @@
"fieldname": "serial_no",
"fieldtype": "Text",
"label": "Serial No",
"no_copy": 1,
"oldfieldname": "serial_no",
"oldfieldtype": "Small Text"
},
@@ -945,19 +949,41 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "pos_invoice_item",
"fieldtype": "Data",
"ignore_user_permissions": 1,
"label": "POS Invoice Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "column_break_vwhb",
"fieldtype": "Column Break"
},
{
"fieldname": "pos_invoice",
"fieldtype": "Link",
"label": "POS Invoice",
"no_copy": 1,
"options": "POS Invoice",
"print_hide": 1,
"search_index": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-11-25 16:27:33.287341",
"modified": "2025-03-12 16:33:52.503777",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -64,6 +64,8 @@ class SalesInvoiceItem(Document):
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
pos_invoice: DF.Link | None
pos_invoice_item: DF.Data | None
price_list_rate: DF.Currency
pricing_rules: DF.SmallText | None
project: DF.Link | None

View File

@@ -13,17 +13,15 @@
"fields": [
{
"fieldname": "voucher_type",
"fieldtype": "Link",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Voucher Type",
"options": "DocType"
"label": "Voucher Type"
},
{
"fieldname": "voucher_name",
"fieldtype": "Dynamic Link",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Voucher Name",
"options": "voucher_type"
"label": "Voucher Name"
},
{
"fieldname": "taxable_amount",
@@ -36,7 +34,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-01-13 13:40:41.479208",
"modified": "2025-02-05 16:39:14.863698",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Withheld Vouchers",

View File

@@ -18,8 +18,8 @@ class TaxWithheldVouchers(Document):
parentfield: DF.Data
parenttype: DF.Data
taxable_amount: DF.Currency
voucher_name: DF.DynamicLink | None
voucher_type: DF.Link | None
voucher_name: DF.Data | None
voucher_type: DF.Data | None
# end: auto-generated types
pass

View File

@@ -10,6 +10,7 @@ frappe.ui.form.on("Tax Withholding Category", {
filters: {
company: child.company,
root_type: ["in", ["Asset", "Liability"]],
is_group: 0,
},
};
}

View File

@@ -36,27 +36,38 @@ class TaxWithholdingCategory(Document):
def validate(self):
self.validate_dates()
self.validate_accounts()
self.validate_companies_and_accounts()
self.validate_thresholds()
def validate_dates(self):
last_date = None
for d in self.get("rates"):
last_to_date = None
rates = sorted(self.get("rates"), key=lambda d: getdate(d.from_date))
for d in rates:
if getdate(d.from_date) >= getdate(d.to_date):
frappe.throw(_("Row #{0}: From Date cannot be before To Date").format(d.idx))
# validate overlapping of dates
if last_date and getdate(d.to_date) < getdate(last_date):
if last_to_date and getdate(d.from_date) < getdate(last_to_date):
frappe.throw(_("Row #{0}: Dates overlapping with other row").format(d.idx))
def validate_accounts(self):
existing_accounts = []
last_to_date = d.to_date
def validate_companies_and_accounts(self):
existing_accounts = set()
companies = set()
for d in self.get("accounts"):
# validate duplicate company
if d.get("company") in companies:
frappe.throw(_("Company {0} added multiple times").format(frappe.bold(d.get("company"))))
companies.add(d.get("company"))
# validate duplicate account
if d.get("account") in existing_accounts:
frappe.throw(_("Account {0} added multiple times").format(frappe.bold(d.get("account"))))
validate_account_head(d.idx, d.get("account"), d.get("company"))
existing_accounts.append(d.get("account"))
existing_accounts.add(d.get("account"))
def validate_thresholds(self):
for d in self.get("rates"):
@@ -436,6 +447,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
tax_details.get("tax_withholding_category"),
company,
),
as_dict=1,
)
for d in journal_entries_details:

View File

@@ -519,7 +519,7 @@ class TestTaxWithholdingCategory(FrappeTestCase):
payment = get_payment_entry(order.doctype, order.name)
payment.apply_tax_withholding_amount = 1
payment.tax_withholding_category = "Cumulative Threshold TDS"
payment.submit()
payment.save().submit()
self.assertEqual(payment.taxes[0].tax_amount, 4000)
def test_multi_category_single_supplier(self):

View File

@@ -81,6 +81,10 @@ def make_acc_dimensions_offsetting_entry(gl_map):
"credit_in_account_currency": credit,
"remarks": _("Offsetting for Accounting Dimension") + f" - {dimension.name}",
"against_voucher": None,
"account_currency": dimension.account_currency,
# Party Type and Party are restricted to Receivable and Payable accounts
"party_type": None,
"party": None,
}
)
offsetting_entry["against_voucher_type"] = None
@@ -108,6 +112,9 @@ def get_accounting_dimensions_for_offsetting_entry(gl_map, company):
accounting_dimensions_to_offset = []
for acc_dimension in acc_dimensions:
values = set([entry.get(acc_dimension.fieldname) for entry in gl_map])
acc_dimension.account_currency = frappe.get_cached_value(
"Account", acc_dimension.offsetting_account, "account_currency"
)
if len(values) > 1:
accounting_dimensions_to_offset.append(acc_dimension)
@@ -430,7 +437,7 @@ def process_debit_credit_difference(gl_map):
voucher_no = gl_map[0].voucher_no
allowance = get_debit_credit_allowance(voucher_type, precision)
debit_credit_diff = get_debit_credit_difference(gl_map, precision)
debit_credit_diff, trx_cur_debit_credit_diff = get_debit_credit_difference(gl_map, precision)
if abs(debit_credit_diff) > allowance:
if not (
@@ -441,9 +448,9 @@ def process_debit_credit_difference(gl_map):
raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
make_round_off_gle(gl_map, debit_credit_diff, precision)
make_round_off_gle(gl_map, debit_credit_diff, trx_cur_debit_credit_diff, precision)
debit_credit_diff = get_debit_credit_difference(gl_map, precision)
debit_credit_diff, trx_cur_debit_credit_diff = get_debit_credit_difference(gl_map, precision)
if abs(debit_credit_diff) > allowance:
if not (
voucher_type == "Journal Entry"
@@ -455,14 +462,23 @@ def process_debit_credit_difference(gl_map):
def get_debit_credit_difference(gl_map, precision):
debit_credit_diff = 0.0
trx_cur_debit_credit_diff = 0
for entry in gl_map:
entry.debit = flt(entry.debit, precision)
entry.credit = flt(entry.credit, precision)
debit_credit_diff += entry.debit - entry.credit
debit_credit_diff = flt(debit_credit_diff, precision)
entry.debit_in_transaction_currency = flt(entry.debit_in_transaction_currency, precision)
entry.credit_in_transaction_currency = flt(entry.credit_in_transaction_currency, precision)
trx_cur_debit_credit_diff += (
entry.debit_in_transaction_currency - entry.credit_in_transaction_currency
)
return debit_credit_diff
debit_credit_diff = flt(debit_credit_diff, precision)
trx_cur_debit_credit_diff = flt(trx_cur_debit_credit_diff, precision)
return debit_credit_diff, trx_cur_debit_credit_diff
def get_debit_credit_allowance(voucher_type, precision):
@@ -489,7 +505,7 @@ def has_opening_entries(gl_map: list) -> bool:
return False
def make_round_off_gle(gl_map, debit_credit_diff, precision):
def make_round_off_gle(gl_map, debit_credit_diff, trx_cur_debit_credit_diff, precision):
round_off_account, round_off_cost_center, round_off_for_opening = get_round_off_account_and_cost_center(
gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no
)
@@ -534,6 +550,12 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
"credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
"debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
"credit": debit_credit_diff if debit_credit_diff > 0 else 0,
"debit_in_transaction_currency": abs(trx_cur_debit_credit_diff)
if trx_cur_debit_credit_diff < 0
else 0,
"credit_in_transaction_currency": trx_cur_debit_credit_diff
if trx_cur_debit_credit_diff > 0
else 0,
"cost_center": round_off_cost_center,
"party_type": None,
"party": None,

View File

@@ -572,12 +572,13 @@ def validate_party_accounts(doc):
@frappe.whitelist()
def get_due_date(posting_date, party_type, party, company=None, bill_date=None):
def get_due_date(posting_date, party_type, party, company=None, bill_date=None, template_name=None):
"""Get due date from `Payment Terms Template`"""
due_date = None
if (bill_date or posting_date) and party:
due_date = bill_date or posting_date
template_name = get_payment_terms_template(party, party_type, company)
if not template_name:
template_name = get_payment_terms_template(party, party_type, company)
if template_name:
due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")

View File

@@ -0,0 +1,161 @@
{%- macro add_header(page_num, max_pages, doc, letter_head, no_letterhead, footer, print_settings=None, print_heading_template=None) -%}
{% if letter_head and not no_letterhead %}
<div class="letter-head">{{ letter_head }}</div>
{% endif %}
{% if print_heading_template %}
{{ frappe.render_template(print_heading_template, {"doc":doc}) }}
{% else %}
{% endif %}
{%- if doc.meta.is_submittable and doc.docstatus==2-%}
<div class="text-center" document-status="cancelled">
<h4 style="margin: 0px;">{{ _("CANCELLED") }}</h4>
</div>
{%- endif -%}
{%- endmacro -%}
{% for page in layout %}
<div class="page-break">
<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
{{ add_header(loop.index, layout|len, doc, letter_head, no_letterhead, footer, print_settings) }}
</div>
<style>
.taxes-section .order-taxes.mt-5{
margin-top: 0px !important;
}
.taxes-section .order-taxes .border-btm.pb-5{
padding-bottom: 0px !important;
}
.print-format label{
color: #74808b;
font-size: 12px;
margin-bottom: 4px;
}
</style>
{% if print_settings.repeat_header_footer %}
<div id="footer-html" class="visible-pdf">
{% if not no_letterhead and footer %}
<div class="letter-head-footer">
{{ footer }}
</div>
{% endif %}
<p class="text-center small page-number visible-pdf">
{{ _("Page {0} of {1}").format('<span class="page"></span>', '<span class="topage"></span>') }}
</p>
</div>
{% endif %}
<div class="row section-break" style="margin-bottom: 10px;">
<div class="col-xs-6 p-0">
<div class="col-xs-12 value text-uppercase"><b>{{ doc.customer }}</b></div>
<div class="col-xs-12">
{{ doc.address_display }}
</div>
<div class="col-xs-12">
{{ _("Contact: ")+doc.contact_display if doc.contact_display else '' }}
</div>
<div class="col-xs-12">
{{ _("Mobile: ")+doc.contact_mobile if doc.contact_mobile else '' }}
</div>
</div>
<div class="col-xs-3"></div>
<div class="col-xs-3" style="padding-left: 5px;">
<div>
<div><label>{{ _("Invoice ID") }}</label></div>
<div>{{ doc.name }}</div>
</div>
<div style="margin-top: 20px;">
<div><label>{{ _("Invoice Date") }}</label></div>
<div>{{ frappe.utils.format_date(doc.posting_date) }}</div>
</div>
<div style="margin-top: 20px;">
<div><label>{{ _("Due Date") }}</label></div>
<div>{{ frappe.utils.format_date(doc.due_date) }}</div>
</div>
</div>
</div>
<div class="section-break">
<table class="table table-bordered table-condensed mb-0" style="width: 100%; border-collapse: collapse; font-size: 12px;">
<colgroup>
<col style="width: 5%">
<col style="width: 45%">
<col style="width: 10%">
<col style="width: 20%">
<col style="width: 20%">
</colgroup>
<thead>
<tr>
<th class="text-uppercase" style="text-align:center">{{ _("Sr") }}</th>
<th class="text-uppercase" style="text-align:center">{{ _("Details") }}</th>
<th class="text-uppercase" style="text-align:center">{{ _("Qty") }}</th>
<th class="text-uppercase" style="text-align:right">{{ _("Rate") }}</th>
<th class="text-uppercase" style="text-align:right">{{ _("Amount") }}</th>
</tr>
</thead>
{% for item in doc.items %}
<tr>
<td style="text-align:center">{{ loop.index }}</td>
<td>
<b>{{ item.item_code }}: {{ item.item_name }}</b>
{% if (item.description != item.item_name) %}
<br>{{ item.description }}
{% endif %}
</td>
<td style="text-align: center;">
{{ item.get_formatted("qty", 0) }}
{{ item.get_formatted("uom", 0) }}
</td>
<td style="text-align: right;">{{ item.get_formatted("net_rate", doc) }}</td>
<td style="text-align: right;">{{ item.get_formatted("net_amount", doc) }}</td>
</tr>
{% endfor %}
</table>
<!-- total -->
<div class="row">
<div class="col-xs-6">
<div>
<label>{{ _("Amount in Words") }}</label>
{{ doc.in_words }}
</div>
<div style="margin-top: 20px;">
<label>{{ _("Payment Status") }}</label>
{{ doc.status }}
</div>
</div>
<div class="col-xs-6">
<div class="row section-break">
<div class="col-xs-7"><div>{{ _("Sub Total") }}</div></div>
<div class="col-xs-5" style="text-align: right;">{{ doc.get_formatted("net_total", doc) }}</div>
</div>
<div>
{% for d in doc.taxes %}
{% if d.tax_amount %}
<div class="row">
<div class="col-xs-8"><div>{{ _(d.description) }}</div></div>
<div class="col-xs-4" style="text-align: right;">{{ d.get_formatted("tax_amount") }}</div>
</div>
{% endif %}
{% endfor %}
</div>
<div class="row">
<div class="col-xs-7"><div>{{ _("Total") }}</div></div>
<div class="col-xs-5" style="text-align: right;">{{ doc.get_formatted("grand_total", doc) }}</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="row important data-field">
<div class="col-xs-12"><label>{{ _("Terms and Conditions") }}: </label></div>
<div class="col-xs-12">{{ doc.terms if doc.terms else '' }}</div>
</div>
</div>
</div>
</div>
{% endfor %}

View File

@@ -0,0 +1,32 @@
{
"absolute_value": 0,
"align_labels_right": 0,
"creation": "2025-01-22 16:23:51.012200",
"css": "",
"custom_format": 0,
"default_print_language": "en",
"disabled": 0,
"doc_type": "Sales Invoice",
"docstatus": 0,
"doctype": "Print Format",
"font": "",
"font_size": 14,
"idx": 0,
"line_breaks": 0,
"margin_bottom": 0.0,
"margin_left": 0.0,
"margin_right": 0.0,
"margin_top": 0.0,
"modified": "2025-01-22 16:23:51.012200",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Print",
"owner": "Administrator",
"page_number": "Hide",
"print_format_builder": 0,
"print_format_builder_beta": 0,
"print_format_type": "Jinja",
"raw_printing": 0,
"show_section_headings": 0,
"standard": "Yes"
}

View File

@@ -520,7 +520,7 @@ class ReceivablePayableReport:
ps.description, ps.paid_amount, ps.discounted_amount
from `tab{row.voucher_type}` si, `tabPayment Schedule` ps
where
si.name = ps.parent and
si.name = ps.parent and ps.parenttype = '{row.voucher_type}' and
si.name = %s and
si.is_return = 0
order by ps.paid_amount desc, due_date
@@ -729,11 +729,13 @@ class ReceivablePayableReport:
"company": self.filters.company,
"update_outstanding_for_self": 0,
}
or_filters = {}
for party_type in self.party_type:
if party_type := self.filters.party_type:
party_field = scrub(party_type)
if self.filters.get(party_field):
or_filters.update({party_field: self.filters.get(party_field)})
if parties := self.filters.get("party"):
or_filters.update({party_field: ["in", parties]})
self.return_entries = frappe._dict(
frappe.get_all(
doctype, filters=filters, or_filters=or_filters, fields=["name", "return_against"], as_list=1

View File

@@ -50,6 +50,7 @@ def get_group_by_asset_category_data(filters):
flt(row.accumulated_depreciation_as_on_from_date)
+ flt(row.depreciation_amount_during_the_period)
- flt(row.depreciation_eliminated_during_the_period)
- flt(row.depreciation_eliminated_via_reversal)
)
row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt(
@@ -247,6 +248,7 @@ def get_group_by_asset_data(filters):
flt(row.accumulated_depreciation_as_on_from_date)
+ flt(row.depreciation_amount_during_the_period)
- flt(row.depreciation_eliminated_during_the_period)
- flt(row.depreciation_eliminated_via_reversal)
)
row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt(
@@ -276,6 +278,7 @@ def get_assets_for_grouped_by_category(filters):
f"""
SELECT results.asset_category,
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
sum(results.depreciation_eliminated_via_reversal) as depreciation_eliminated_via_reversal,
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category,
@@ -284,6 +287,11 @@ def get_assets_for_grouped_by_category(filters):
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when gle.posting_date <= %(to_date)s and ifnull(a.disposal_date, 0) = 0 then
gle.credit
else
0
end), 0) as depreciation_eliminated_via_reversal,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then
gle.debit
@@ -307,7 +315,6 @@ def get_assets_for_grouped_by_category(filters):
a.docstatus=1
and a.company=%(company)s
and a.purchase_date <= %(to_date)s
and gle.debit != 0
and gle.is_cancelled = 0
and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
{condition} {finance_book_filter}
@@ -319,6 +326,7 @@ def get_assets_for_grouped_by_category(filters):
else
a.opening_accumulated_depreciation
end), 0) as accumulated_depreciation_as_on_from_date,
0 as depreciation_eliminated_via_reversal,
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
a.opening_accumulated_depreciation
else
@@ -354,6 +362,7 @@ def get_assets_for_grouped_by_asset(filters):
f"""
SELECT results.name as asset,
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
sum(results.depreciation_eliminated_via_reversal) as depreciation_eliminated_via_reversal,
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.name as name,
@@ -362,6 +371,11 @@ def get_assets_for_grouped_by_asset(filters):
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when gle.posting_date <= %(to_date)s and ifnull(a.disposal_date, 0) = 0 then
gle.credit
else
0
end), 0) as depreciation_eliminated_via_reversal,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then
gle.debit
@@ -385,7 +399,6 @@ def get_assets_for_grouped_by_asset(filters):
a.docstatus=1
and a.company=%(company)s
and a.purchase_date <= %(to_date)s
and gle.debit != 0
and gle.is_cancelled = 0
and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
{finance_book_filter} {condition}
@@ -397,6 +410,7 @@ def get_assets_for_grouped_by_asset(filters):
else
a.opening_accumulated_depreciation
end), 0) as accumulated_depreciation_as_on_from_date,
0 as depreciation_as_on_from_date_credit,
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
a.opening_accumulated_depreciation
else
@@ -503,6 +517,12 @@ def get_columns(filters):
"fieldtype": "Currency",
"width": 270,
},
{
"label": _("Depreciation eliminated via reversal"),
"fieldname": "depreciation_eliminated_via_reversal",
"fieldtype": "Currency",
"width": 270,
},
{
"label": _("Net Asset value as on") + " " + formatdate(filters.day_before_from_date),
"fieldname": "net_asset_value_as_on_from_date",

View File

@@ -27,6 +27,7 @@ def get_report_filters(report_filters):
["Purchase Invoice", "docstatus", "=", 1],
["Purchase Invoice", "per_received", "<", 100],
["Purchase Invoice", "update_stock", "=", 0],
["Purchase Invoice", "is_opening", "!=", "Yes"],
]
if report_filters.get("purchase_invoice"):

View File

@@ -263,6 +263,7 @@ def get_actual_details(name, filters):
and ba.account=gl.account
and b.{budget_against} = gl.{budget_against}
and gl.fiscal_year between %s and %s
and gl.is_cancelled = 0
and b.{budget_against} = %s
and exists(
select

View File

@@ -307,6 +307,7 @@ class Deferred_Revenue_and_Expense_Report:
.where(
(inv.docstatus == 1)
& (deferred_flag_field == 1)
& (inv.company == self.filters.company)
& (
(
(self.period_list[0].from_date >= inv_item.service_start_date)

View File

@@ -2,5 +2,27 @@
// For license information, please see license.txt
frappe.query_reports["Delivered Items To Be Billed"] = {
filters: [],
filters: [
{
label: __("Company"),
fieldname: "company",
fieldtype: "Link",
options: "Company",
reqd: 1,
default: frappe.defaults.get_default("Company"),
},
{
label: __("As on Date"),
fieldname: "posting_date",
fieldtype: "Date",
reqd: 1,
default: frappe.datetime.get_today(),
},
{
label: __("Delivery Note"),
fieldname: "delivery_note",
fieldtype: "Link",
options: "Delivery Note",
},
],
};

View File

@@ -3,6 +3,7 @@
from frappe import _
from pypika import Order
from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
@@ -10,7 +11,7 @@ from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_d
def execute(filters=None):
columns = get_column()
args = get_args()
data = get_ordered_to_be_billed_data(args)
data = get_ordered_to_be_billed_data(args, filters)
return columns, data
@@ -76,13 +77,6 @@ def get_column():
"options": "Project",
"width": 120,
},
{
"label": _("Company"),
"fieldname": "company",
"fieldtype": "Link",
"options": "Company",
"width": 120,
},
]
@@ -92,5 +86,6 @@ def get_args():
"party": "customer",
"date": "posting_date",
"order": "name",
"order_by": "desc",
"order_by": Order.desc,
"reference_field": "delivery_note",
}

View File

@@ -630,7 +630,7 @@ def get_cost_centers_with_children(cost_centers):
def get_columns(periodicity, period_list, accumulated_values=1, company=None, cash_flow=False):
columns = [
{
"fieldname": "account",
"fieldname": "account" if not cash_flow else "section",
"label": _("Account") if not cash_flow else _("Section"),
"fieldtype": "Link",
"options": "Account",

View File

@@ -534,6 +534,7 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, tot
for dim in accounting_dimensions:
keylist.append(gle.get(dim))
keylist.append(gle.get("cost_center"))
keylist.append(gle.get("project"))
key = tuple(keylist)
if key not in consolidated_gle:
@@ -679,10 +680,11 @@ def get_columns(filters):
{"label": _("Against Account"), "fieldname": "against", "width": 120},
{"label": _("Party Type"), "fieldname": "party_type", "width": 100},
{"label": _("Party"), "fieldname": "party", "width": 100},
{"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100},
]
if filters.get("include_dimensions"):
columns.append({"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100})
for dim in get_accounting_dimensions(as_list=False):
columns.append(
{"label": _(dim.label), "options": dim.label, "fieldname": dim.fieldname, "width": 100}

View File

@@ -4,11 +4,12 @@
import frappe
from frappe.model.meta import get_field_precision
from frappe.query_builder.functions import IfNull, Round
from erpnext import get_default_currency
def get_ordered_to_be_billed_data(args):
def get_ordered_to_be_billed_data(args, filters=None):
doctype, party = args.get("doctype"), args.get("party")
child_tab = doctype + " Item"
precision = (
@@ -18,47 +19,57 @@ def get_ordered_to_be_billed_data(args):
or 2
)
project_field = get_project_field(doctype, party)
doctype = frappe.qb.DocType(doctype)
child_doctype = frappe.qb.DocType(child_tab)
return frappe.db.sql(
"""
Select
`{parent_tab}`.name, `{parent_tab}`.{date_field},
`{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
`{child_tab}`.item_code,
`{child_tab}`.base_amount,
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)),
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0)),
(`{child_tab}`.base_amount -
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)) -
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))),
`{child_tab}`.item_name, `{child_tab}`.description,
{project_field}, `{parent_tab}`.company
from
`{parent_tab}`, `{child_tab}`
where
`{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1
and `{parent_tab}`.status not in ('Closed', 'Completed')
and `{child_tab}`.amount > 0
and (`{child_tab}`.base_amount -
round(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) -
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))) > 0
order by
`{parent_tab}`.{order} {order_by}
""".format(
parent_tab="tab" + doctype,
child_tab="tab" + child_tab,
precision=precision,
party=party,
date_field=args.get("date"),
project_field=project_field,
order=args.get("order"),
order_by=args.get("order_by"),
docname = filters.get(args.get("reference_field"), None)
project_field = get_project_field(doctype, child_doctype, party)
query = (
frappe.qb.from_(doctype)
.inner_join(child_doctype)
.on(doctype.name == child_doctype.parent)
.select(
doctype.name,
doctype[args.get("date")].as_("date"),
doctype[party],
doctype[party + "_name"],
child_doctype.item_code,
child_doctype.base_amount.as_("amount"),
(child_doctype.billed_amt * IfNull(doctype.conversion_rate, 1)).as_("billed_amount"),
(child_doctype.base_rate * IfNull(child_doctype.returned_qty, 0)).as_("returned_amount"),
(
child_doctype.base_amount
- (child_doctype.billed_amt * IfNull(doctype.conversion_rate, 1))
- (child_doctype.base_rate * IfNull(child_doctype.returned_qty, 0))
).as_("pending_amount"),
child_doctype.item_name,
child_doctype.description,
project_field,
)
.where(
(doctype.docstatus == 1)
& (doctype.status.notin(["Closed", "Completed"]))
& (doctype.company == filters.get("company"))
& (doctype.posting_date <= filters.get("posting_date"))
& (child_doctype.amount > 0)
& (
child_doctype.base_amount
- Round(child_doctype.billed_amt * IfNull(doctype.conversion_rate, 1), precision)
- (child_doctype.base_rate * IfNull(child_doctype.returned_qty, 0))
)
> 0
)
.orderby(doctype[args.get("order")], order=args.get("order_by"))
)
if docname:
query = query.where(doctype.name == docname)
def get_project_field(doctype, party):
return query.run(as_dict=True)
def get_project_field(doctype, child_doctype, party):
if party == "supplier":
doctype = doctype + " Item"
return "`tab%s`.project" % (doctype)
return child_doctype.project
return doctype.project

View File

@@ -2,5 +2,27 @@
// For license information, please see license.txt
frappe.query_reports["Received Items To Be Billed"] = {
filters: [],
filters: [
{
label: __("Company"),
fieldname: "company",
fieldtype: "Link",
options: "Company",
reqd: 1,
default: frappe.defaults.get_default("Company"),
},
{
label: __("As on Date"),
fieldname: "posting_date",
fieldtype: "Date",
reqd: 1,
default: frappe.datetime.get_today(),
},
{
label: __("Purchase Receipt"),
fieldname: "purchase_receipt",
fieldtype: "Link",
options: "Purchase Receipt",
},
],
};

View File

@@ -3,6 +3,7 @@
from frappe import _
from pypika import Order
from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data
@@ -10,7 +11,7 @@ from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_d
def execute(filters=None):
columns = get_column()
args = get_args()
data = get_ordered_to_be_billed_data(args)
data = get_ordered_to_be_billed_data(args, filters)
return columns, data
@@ -76,13 +77,6 @@ def get_column():
"options": "Project",
"width": 120,
},
{
"label": _("Company"),
"fieldname": "company",
"fieldtype": "Link",
"options": "Company",
"width": 120,
},
]
@@ -92,5 +86,6 @@ def get_args():
"party": "supplier",
"date": "posting_date",
"order": "name",
"order_by": "desc",
"order_by": Order.desc,
"reference_field": "purchase_receipt",
}

View File

@@ -1417,7 +1417,7 @@ def repost_gle_for_stock_vouchers(
if not warehouse_account:
warehouse_account = get_warehouse_account_map(company)
stock_vouchers = sort_stock_vouchers_by_posting_date(stock_vouchers)
stock_vouchers = sort_stock_vouchers_by_posting_date(stock_vouchers, company=company)
if repost_doc and repost_doc.gl_reposting_index:
# Restore progress
stock_vouchers = stock_vouchers[cint(repost_doc.gl_reposting_index) :]
@@ -1470,7 +1470,9 @@ def _delete_accounting_ledger_entries(voucher_type, voucher_no):
_delete_pl_entries(voucher_type, voucher_no)
def sort_stock_vouchers_by_posting_date(stock_vouchers: list[tuple[str, str]]) -> list[tuple[str, str]]:
def sort_stock_vouchers_by_posting_date(
stock_vouchers: list[tuple[str, str]], company=None
) -> list[tuple[str, str]]:
sle = frappe.qb.DocType("Stock Ledger Entry")
voucher_nos = [v[1] for v in stock_vouchers]
@@ -1481,7 +1483,12 @@ def sort_stock_vouchers_by_posting_date(stock_vouchers: list[tuple[str, str]]) -
.groupby(sle.voucher_type, sle.voucher_no)
.orderby(sle.posting_datetime)
.orderby(sle.creation)
).run(as_dict=True)
)
if company:
sles = sles.where(sle.company == company)
sles = sles.run(as_dict=True)
sorted_vouchers = [(sle.voucher_type, sle.voucher_no) for sle in sles]
unknown_vouchers = set(stock_vouchers) - set(sorted_vouchers)

View File

@@ -93,7 +93,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Accounts Payable",
"link_count": 0,
"link_to": "Accounts Payable",
@@ -103,7 +103,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Accounts Payable Summary",
"link_count": 0,
"link_to": "Accounts Payable Summary",
@@ -113,7 +113,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Purchase Register",
"link_count": 0,
"link_to": "Purchase Register",
@@ -123,7 +123,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Item-wise Purchase Register",
"link_count": 0,
"link_to": "Item-wise Purchase Register",
@@ -133,7 +133,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Purchase Order Analysis",
"link_count": 0,
"link_to": "Purchase Order Analysis",
@@ -143,7 +143,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Received Items To Be Billed",
"link_count": 0,
"link_to": "Received Items To Be Billed",
@@ -153,7 +153,7 @@
},
{
"hidden": 0,
"is_query_report": 0,
"is_query_report": 1,
"label": "Supplier Ledger Summary",
"link_count": 0,
"link_to": "Supplier Ledger Summary",

View File

@@ -225,7 +225,7 @@
{
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"label": "Gross Purchase Amount",
"label": "Net Purchase Amount",
"mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)",
"options": "Company:company:default_currency"
},
@@ -592,7 +592,7 @@
"link_fieldname": "target_asset"
}
],
"modified": "2025-02-11 16:01:56.140904",
"modified": "2025-02-20 14:09:05.421913",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@@ -143,14 +143,19 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
}
}
set_consumed_stock_items_tagged_to_wip_composite_asset(asset) {
set_consumed_stock_items_tagged_to_wip_composite_asset(target_asset) {
var me = this;
if (asset) {
if (target_asset) {
return me.frm.call({
method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_items_tagged_to_wip_composite_asset",
args: {
asset: asset,
params: {
target_asset: target_asset,
finance_book: me.frm.doc.finance_book,
posting_date: me.frm.doc.posting_date,
posting_time: me.frm.doc.posting_time,
},
},
callback: function (r) {
if (!r.exc && r.message) {

View File

@@ -123,6 +123,7 @@ class AssetCapitalization(StockController):
self.make_bundle_using_old_serial_batch_fields()
self.update_stock_ledger()
self.make_gl_entries()
self.repost_future_sle_and_gle()
self.update_target_asset()
def on_cancel(self):
@@ -136,6 +137,7 @@ class AssetCapitalization(StockController):
)
self.update_stock_ledger()
self.make_gl_entries()
self.repost_future_sle_and_gle()
self.restore_consumed_asset_items()
def set_title(self):
@@ -856,7 +858,10 @@ def get_service_item_details(args):
@frappe.whitelist()
def get_items_tagged_to_wip_composite_asset(asset):
def get_items_tagged_to_wip_composite_asset(params):
if isinstance(params, str):
params = json.loads(params)
fields = [
"item_code",
"item_name",
@@ -871,25 +876,66 @@ def get_items_tagged_to_wip_composite_asset(asset):
"amount",
"is_fixed_asset",
"parent",
"name",
]
pr_items = frappe.get_all(
"Purchase Receipt Item", filters={"wip_composite_asset": asset, "docstatus": 1}, fields=fields
"Purchase Receipt Item",
filters={"wip_composite_asset": params.get("target_asset"), "docstatus": 1},
fields=fields,
)
stock_items = []
asset_items = []
for d in pr_items:
if not d.is_fixed_asset:
stock_items.append(frappe._dict(d))
stock_item = process_stock_item(d)
if stock_item:
stock_items.append(stock_item)
else:
asset_details = frappe.db.get_value(
"Asset",
{"item_code": d.item_code, "purchase_receipt": d.parent},
["name as asset", "asset_name"],
as_dict=1,
)
d.update(asset_details)
asset_items.append(frappe._dict(d))
asset_item = process_fixed_asset(d)
if asset_item:
asset_items.append(asset_item)
return stock_items, asset_items
def process_stock_item(d):
stock_capitalized = frappe.db.exists(
"Asset Capitalization Stock Item",
{
"purchase_receipt_item": d.name,
"parentfield": "stock_items",
"parenttype": "Asset Capitalization",
"docstatus": 1,
},
)
if stock_capitalized:
return None
stock_item_data = frappe._dict(d)
stock_item_data.purchase_receipt_item = d.name
return stock_item_data
def process_fixed_asset(d):
asset_details = frappe.db.get_value(
"Asset",
{
"item_code": d.item_code,
"purchase_receipt": d.parent,
"status": ("not in", ["Draft", "Scrapped", "Sold", "Capitalized"]),
},
["name as asset", "asset_name", "company"],
as_dict=1,
)
if asset_details:
asset_details.update(d)
asset_details.update(get_consumed_asset_details(asset_details))
d.update(asset_details)
return frappe._dict(d)
return None

View File

@@ -10,12 +10,13 @@
"column_break_3",
"warehouse",
"section_break_6",
"purchase_receipt_item",
"stock_qty",
"stock_uom",
"actual_qty",
"column_break_9",
"valuation_rate",
"amount",
"stock_uom",
"batch_and_serial_no_section",
"serial_and_batch_bundle",
"use_serial_batch_fields",
@@ -53,14 +54,14 @@
{
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"label": "Qty and Rate"
"label": "Purchase Details"
},
{
"columns": 1,
"fieldname": "stock_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Qty",
"label": "Quantity",
"non_negative": 1
},
{
@@ -172,18 +173,26 @@
{
"fieldname": "column_break_mbuv",
"fieldtype": "Column Break"
},
{
"fieldname": "purchase_receipt_item",
"fieldtype": "Data",
"hidden": 1,
"label": "Purchase Receipt Item"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-06-26 17:06:22.564438",
"modified": "2025-03-05 12:46:01.074742",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Capitalization Stock Item",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"track_changes": 1

View File

@@ -23,6 +23,7 @@ class AssetCapitalizationStockItem(Document):
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
purchase_receipt_item: DF.Data | None
serial_and_batch_bundle: DF.Link | None
serial_no: DF.Text | None
stock_qty: DF.Float

View File

@@ -404,7 +404,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
);
}
} else {
if (!doc.items.every((item) => item.qty == item.sco_qty)) {
if (!doc.items.every((item) => item.qty == item.subcontracted_quantity)) {
this.frm.add_custom_button(
__("Subcontracting Order"),
() => {

View File

@@ -898,7 +898,7 @@ def is_po_fully_subcontracted(po_name):
query = (
frappe.qb.from_(table)
.select(table.name)
.where((table.parent == po_name) & (table.qty != table.sco_qty))
.where((table.parent == po_name) & (table.qty != table.subcontracted_quantity))
)
return not query.run(as_dict=True)
@@ -945,7 +945,7 @@ def get_mapped_subcontracting_order(source_name, target_doc=None):
"material_request_item": "material_request_item",
},
"field_no_map": ["qty", "fg_item_qty", "amount"],
"condition": lambda item: item.qty != item.sco_qty,
"condition": lambda item: item.qty != item.subcontracted_quantity,
},
},
target_doc,

View File

@@ -920,6 +920,7 @@ class TestPurchaseOrder(FrappeTestCase):
automatically_fetch_payment_terms(enable=0)
def test_internal_transfer_flow(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
make_inter_company_purchase_invoice,
)
@@ -935,8 +936,16 @@ class TestPurchaseOrder(FrappeTestCase):
prepare_data_for_internal_transfer()
supplier = "_Test Internal Supplier 2"
create_cost_center(
cost_center_name="_Test Cost Center for perpetual inventory Account",
company="_Test Company with perpetual inventory",
)
mr = make_material_request(
qty=2, company="_Test Company with perpetual inventory", warehouse="Stores - TCP1"
qty=2,
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
cost_center="_Test Cost Center for perpetual inventory Account - TCP1",
)
po = create_purchase_order(
@@ -1076,9 +1085,9 @@ class TestPurchaseOrder(FrappeTestCase):
# Test - 2: Subcontracted Quantity for the PO Items of each line item should be updated accordingly
po.reload()
self.assertEqual(po.items[0].sco_qty, 5)
self.assertEqual(po.items[1].sco_qty, 0)
self.assertEqual(po.items[2].sco_qty, 12.5)
self.assertEqual(po.items[0].subcontracted_quantity, 5)
self.assertEqual(po.items[1].subcontracted_quantity, 0)
self.assertEqual(po.items[2].subcontracted_quantity, 12.5)
# Test - 3: Amount for both FG Item and its Service Item should be updated correctly based on change in Quantity
self.assertEqual(sco.items[0].amount, 2000)
@@ -1114,10 +1123,10 @@ class TestPurchaseOrder(FrappeTestCase):
# Test - 8: Subcontracted Quantity for each PO Item should be subtracted if SCO gets cancelled
po.reload()
self.assertEqual(po.items[2].sco_qty, 25)
self.assertEqual(po.items[2].subcontracted_quantity, 25)
sco.cancel()
po.reload()
self.assertEqual(po.items[2].sco_qty, 12.5)
self.assertEqual(po.items[2].subcontracted_quantity, 12.5)
sco = make_subcontracting_order(po.name)
sco.save()

View File

@@ -26,7 +26,7 @@
"quantity_and_rate",
"qty",
"stock_uom",
"sco_qty",
"subcontracted_quantity",
"col_break2",
"uom",
"conversion_factor",
@@ -913,7 +913,8 @@
},
{
"allow_on_submit": 1,
"fieldname": "sco_qty",
"depends_on": "eval:parent.is_subcontracted && !parent.is_old_subcontracting_flow",
"fieldname": "subcontracted_quantity",
"fieldtype": "Float",
"label": "Subcontracted Quantity",
"no_copy": 1,
@@ -921,11 +922,12 @@
"read_only": 1
}
],
"grid_page_length": 50,
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2025-02-18 12:35:04.432636",
"modified": "2025-03-13 17:27:43.468602",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",
@@ -933,9 +935,10 @@
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"row_format": "Dynamic",
"search_fields": "item_name",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -80,10 +80,10 @@ class PurchaseOrderItem(Document):
sales_order_item: DF.Data | None
sales_order_packed_item: DF.Data | None
schedule_date: DF.Date
sco_qty: DF.Float
stock_qty: DF.Float
stock_uom: DF.Link
stock_uom_rate: DF.Currency
subcontracted_quantity: DF.Float
supplier_part_no: DF.Data | None
supplier_quotation: DF.Link | None
supplier_quotation_item: DF.Link | None

View File

@@ -19,6 +19,10 @@ frappe.query_reports["Purchase Order Analysis"] = {
width: "80",
reqd: 1,
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
on_change: (report) => {
report.set_filter_value("name", []);
report.refresh();
},
},
{
fieldname: "to_date",
@@ -27,6 +31,10 @@ frappe.query_reports["Purchase Order Analysis"] = {
width: "80",
reqd: 1,
default: frappe.datetime.get_today(),
on_change: (report) => {
report.set_filter_value("name", []);
report.refresh();
},
},
{
fieldname: "project",
@@ -38,13 +46,17 @@ frappe.query_reports["Purchase Order Analysis"] = {
{
fieldname: "name",
label: __("Purchase Order"),
fieldtype: "Link",
fieldtype: "MultiSelectList",
width: "80",
options: "Purchase Order",
get_query: () => {
return {
filters: { docstatus: 1 },
};
get_data: function (txt) {
let filters = { docstatus: 1 };
const from_date = frappe.query_report.get_filter_value("from_date");
const to_date = frappe.query_report.get_filter_value("to_date");
if (from_date && to_date) filters["transaction_date"] = ["between", [from_date, to_date]];
return frappe.db.get_link_options("Purchase Order", txt, filters);
},
},
{
@@ -52,9 +64,16 @@ frappe.query_reports["Purchase Order Analysis"] = {
label: __("Status"),
fieldtype: "MultiSelectList",
width: "80",
options: ["To Pay", "To Bill", "To Receive", "To Receive and Bill", "Completed"],
options: ["To Pay", "To Bill", "To Receive", "To Receive and Bill", "Completed", "Closed"],
get_data: function (txt) {
let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"];
let status = [
"To Pay",
"To Bill",
"To Receive",
"To Receive and Bill",
"Completed",
"Closed",
];
let options = [];
for (let option of status) {
options.push({

View File

@@ -70,14 +70,16 @@ def get_data(filters):
po.company,
po_item.name,
)
.where((po_item.parent == po.name) & (po.status.notin(("Stopped", "Closed"))) & (po.docstatus == 1))
.where((po_item.parent == po.name) & (po.status.notin(("Stopped", "On Hold"))) & (po.docstatus == 1))
.groupby(po_item.name)
.orderby(po.transaction_date)
)
for field in ("company", "name"):
if filters.get(field):
query = query.where(po[field] == filters.get(field))
if filters.get("company"):
query = query.where(po.company == filters.get("company"))
if filters.get("name"):
query = query.where(po.name.isin(filters.get("name")))
if filters.get("from_date") and filters.get("to_date"):
query = query.where(po.transaction_date.between(filters.get("from_date"), filters.get("to_date")))

View File

@@ -194,6 +194,14 @@ class AccountsController(TransactionBase):
self.set_incoming_rate()
self.init_internal_values()
# Need to set taxes based on taxes_and_charges template
# before calculating taxes and totals
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
self.validate_tax_account_company()
self.set_taxes_and_charges()
if self.meta.get_field("currency"):
self.calculate_taxes_and_totals()
@@ -204,10 +212,6 @@ class AccountsController(TransactionBase):
self.validate_all_documents_schedule()
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
self.validate_tax_account_company()
self.validate_party()
self.validate_currency()
self.validate_party_account_currency()
@@ -252,8 +256,6 @@ class AccountsController(TransactionBase):
self.validate_deferred_income_expense_account()
self.set_inter_company_account()
self.set_taxes_and_charges()
if self.doctype == "Purchase Invoice":
self.calculate_paid_amount()
# apply tax withholding only if checked and applicable
@@ -269,6 +271,7 @@ class AccountsController(TransactionBase):
self.set_total_in_words()
self.set_default_letter_head()
self.validate_company_in_accounting_dimension()
self.validate_party_address_and_contact()
def set_default_letter_head(self):
if hasattr(self, "letter_head") and not self.letter_head:
@@ -417,7 +420,7 @@ class AccountsController(TransactionBase):
.where(doc_field.fieldname == "company")
).run(as_list=True)
dimension_list = sum(dimension_list, ["Project"])
dimension_list = sum(dimension_list, ["Project", "Cost Center"])
self.validate_company(dimension_list)
for child in self.get_all_children() or []:
@@ -439,6 +442,45 @@ class AccountsController(TransactionBase):
)
)
def validate_party_address_and_contact(self):
party, party_type = None, None
if self.get("customer"):
party, party_type = self.customer, "Customer"
billing_address, shipping_address = (
self.get("customer_address"),
self.get("shipping_address_name"),
)
self.validate_party_address(party, party_type, billing_address, shipping_address)
elif self.get("supplier"):
party, party_type = self.supplier, "Supplier"
billing_address = self.get("supplier_address")
self.validate_party_address(party, party_type, billing_address)
if party and party_type:
self.validate_party_contact(party, party_type)
def validate_party_address(self, party, party_type, billing_address, shipping_address=None):
if billing_address or shipping_address:
party_address = frappe.get_all(
"Dynamic Link",
{"link_doctype": party_type, "link_name": party, "parenttype": "Address"},
pluck="parent",
)
if billing_address and billing_address not in party_address:
frappe.throw(_("Billing Address does not belong to the {0}").format(party))
elif shipping_address and shipping_address not in party_address:
frappe.throw(_("Shipping Address does not belong to the {0}").format(party))
def validate_party_contact(self, party, party_type):
if self.get("contact_person"):
contact = frappe.get_all(
"Dynamic Link",
{"link_doctype": party_type, "link_name": party, "parenttype": "Contact"},
pluck="parent",
)
if self.contact_person and self.contact_person not in contact:
frappe.throw(_("Contact Person does not belong to the {0}").format(party))
def validate_return_against_account(self):
if self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against:
cr_dr_account_field = "debit_to" if self.doctype == "Sales Invoice" else "credit_to"
@@ -821,11 +863,15 @@ class AccountsController(TransactionBase):
and item.get("use_serial_batch_fields")
)
):
if fieldname == "batch_no" and not item.batch_no and not item.is_free_item:
item.set("rate", ret.get("rate"))
item.set("price_list_rate", ret.get("price_list_rate"))
item.set(fieldname, value)
if fieldname == "batch_no" and item.batch_no and not item.is_free_item:
if ret.get("rate"):
item.set("rate", ret.get("rate"))
if not item.get("price_list_rate") and ret.get("price_list_rate"):
item.set("price_list_rate", ret.get("price_list_rate"))
elif fieldname in ["cost_center", "conversion_factor"] and not item.get(
fieldname
):
@@ -1113,20 +1159,19 @@ class AccountsController(TransactionBase):
)
# Update details in transaction currency
gl_dict.update(
{
"transaction_currency": self.get("currency") or self.company_currency,
"transaction_exchange_rate": item.get("exchange_rate", 1)
if self.doctype == "Journal Entry" and item
else self.get("conversion_rate", 1),
"debit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "debit"
),
"credit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "credit"
),
}
)
if self.doctype not in ["Purchase Invoice", "Sales Invoice", "Journal Entry", "Payment Entry"]:
gl_dict.update(
{
"transaction_currency": self.get("currency") or self.company_currency,
"transaction_exchange_rate": self.get("conversion_rate", 1),
"debit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "debit"
),
"credit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "credit"
),
}
)
if not args.get("against_voucher_type") and self.get("against_voucher_type"):
gl_dict.update({"against_voucher_type": self.get("against_voucher_type")})
@@ -2752,6 +2797,11 @@ class AccountsController(TransactionBase):
elif self.doctype == "Payment Entry":
self.make_advance_payment_ledger_for_payment()
def set_transaction_currency_and_rate_in_gl_map(self, gl_entries):
for x in gl_entries:
x["transaction_currency"] = self.currency
x["transaction_exchange_rate"] = self.get("conversion_rate") or 1
@frappe.whitelist()
def get_tax_rate(account_head):
@@ -3281,6 +3331,7 @@ def set_child_tax_template_and_map(item, child_item, parent_doc):
"posting_date": parent_doc.transaction_date,
"tax_category": parent_doc.get("tax_category"),
"company": parent_doc.get("company"),
"base_net_rate": item.get("base_net_rate"),
}
child_item.item_tax_template = _get_item_tax_template(args, item.taxes)
@@ -3655,6 +3706,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
if d.get("schedule_date") and parent_doctype == "Purchase Order":
child_item.schedule_date = d.get("schedule_date")
if d.get("bom_no") and parent_doctype == "Sales Order":
child_item.bom_no = d.get("bom_no")
if flt(child_item.price_list_rate):
if flt(child_item.rate) > flt(child_item.price_list_rate):
# if rate is greater than price_list_rate, set margin

View File

@@ -333,7 +333,7 @@ class BuyingController(SubcontractingController):
net_rate
+ item.item_tax_amount
+ flt(item.landed_cost_voucher_amount)
+ flt(item.get("rate_difference_with_purchase_invoice"))
+ flt(item.get("amount_difference_with_purchase_invoice"))
) / qty_in_stock_uom
else:
item.valuation_rate = 0.0

View File

@@ -837,6 +837,7 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
"posting_date": valid_from,
"tax_category": filters.get("tax_category"),
"company": company,
"base_net_rate": filters.get("base_net_rate"),
}
taxes = _get_item_tax_template(args, taxes, for_validate=True)

View File

@@ -25,9 +25,6 @@ def validate_return(doc):
if doc.return_against:
validate_return_against(doc)
if doc.doctype in ("Sales Invoice", "Purchase Invoice") and not doc.update_stock:
return
validate_returned_items(doc)
@@ -118,7 +115,7 @@ def validate_returned_items(doc):
elif doc.doctype == "Delivery Note":
key = (d.item_code, d.get("dn_detail"))
if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
if d.item_code and (flt(d.qty) <= 0 or flt(d.get("received_qty")) <= 0):
if key not in valid_items:
frappe.msgprint(
_("Row # {0}: Returned Item {1} does not exist in {2} {3}").format(
@@ -160,6 +157,9 @@ def validate_returned_items(doc):
def validate_quantity(doc, key, args, ref, valid_items, already_returned_items):
fields = ["stock_qty"]
if (doc.doctype == "Purchase Invoice" or doc.doctype == "Sales Invoice") and not doc.update_stock:
fields = ["qty"]
if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]:
if not args.get("return_qty_from_rejected_warehouse"):
fields.extend(["received_qty", "rejected_qty"])
@@ -169,13 +169,16 @@ def validate_quantity(doc, key, args, ref, valid_items, already_returned_items):
already_returned_data = already_returned_items.get(key) or {}
company_currency = erpnext.get_company_currency(doc.company)
stock_qty_precision = get_field_precision(
frappe.get_meta(doc.doctype + " Item").get_field("stock_qty"), company_currency
field_precision = get_field_precision(
frappe.get_meta(doc.doctype + " Item").get_field(
"stock_qty" if doc.get("update_stock", "") else "qty"
),
company_currency,
)
for column in fields:
returned_qty = (
flt(already_returned_data.get(column, 0), stock_qty_precision)
flt(already_returned_data.get(column, 0), field_precision)
if len(already_returned_data) > 0
else 0
)
@@ -190,17 +193,17 @@ def validate_quantity(doc, key, args, ref, valid_items, already_returned_items):
reference_qty = ref.get(column) * ref.get("conversion_factor", 1.0)
current_stock_qty = args.get(column) * args.get("conversion_factor", 1.0)
max_returnable_qty = flt(flt(reference_qty, stock_qty_precision) - returned_qty, stock_qty_precision)
max_returnable_qty = flt(flt(reference_qty, field_precision) - returned_qty, field_precision)
label = column.replace("_", " ").title()
if reference_qty:
if flt(args.get(column)) > 0:
frappe.throw(_("{0} must be negative in return document").format(label))
elif returned_qty >= reference_qty and args.get(column):
elif returned_qty >= reference_qty and args.get(column) >= 0:
frappe.throw(
_("Item {0} has already been returned").format(args.item_code), StockOverReturnError
)
elif abs(flt(current_stock_qty, stock_qty_precision)) > max_returnable_qty:
elif abs(flt(current_stock_qty, field_precision)) > max_returnable_qty:
frappe.throw(
_("Row # {0}: Cannot return more than {1} for Item {2}").format(
args.idx, max_returnable_qty, args.item_code

View File

@@ -1234,7 +1234,7 @@ class StockController(AccountsController):
child_tab.item_code,
child_tab.qty,
)
.where(parent_tab.docstatus < 2)
.where(parent_tab.docstatus == 1)
)
if self.doctype == "Purchase Invoice":

View File

@@ -104,18 +104,18 @@ class SubcontractingController(StockController):
)
if (
self.doctype == "Subcontracting Order" and not item.sc_conversion_factor
self.doctype == "Subcontracting Order" and not item.subcontracting_conversion_factor
): # this condition will only be true if user has recently updated from develop branch
service_item_qty = frappe.get_value(
"Subcontracting Order Service Item",
filters={"purchase_order_item": item.purchase_order_item, "parent": self.name},
fieldname=["qty"],
)
item.sc_conversion_factor = service_item_qty / item.qty
item.subcontracting_conversion_factor = service_item_qty / item.qty
if self.doctype not in "Subcontracting Receipt" and item.qty > flt(
get_pending_sco_qty(self.purchase_order).get(item.purchase_order_item)
/ item.sc_conversion_factor,
get_pending_subcontracted_quantity(self.purchase_order).get(item.purchase_order_item)
/ item.subcontracting_conversion_factor,
frappe.get_precision("Purchase Order Item", "qty"),
):
frappe.throw(
@@ -1132,10 +1132,14 @@ def get_item_details(items):
return item_details
def get_pending_sco_qty(po_name):
def get_pending_subcontracted_quantity(po_name):
table = frappe.qb.DocType("Purchase Order Item")
query = frappe.qb.from_(table).select(table.name, table.qty, table.sco_qty).where(table.parent == po_name)
return {item.name: item.qty - item.sco_qty for item in query.run(as_dict=True)}
query = (
frappe.qb.from_(table)
.select(table.name, table.qty, table.subcontracted_quantity)
.where(table.parent == po_name)
)
return {item.name: item.qty - item.subcontracted_quantity for item in query.run(as_dict=True)}
@frappe.whitelist()

View File

@@ -931,6 +931,35 @@ class TestAccountsController(FrappeTestCase):
self.assertEqual(exc_je_for_si, [])
self.assertEqual(exc_je_for_pe, [])
@change_settings("Accounts Settings", {"add_taxes_from_item_tax_template": 1})
def test_18_fetch_taxes_based_on_taxes_and_charges_template(self):
# Create a Sales Taxes and Charges Template
if not frappe.db.exists("Sales Taxes and Charges Template", "_Test Tax - _TC"):
doc = frappe.new_doc("Sales Taxes and Charges Template")
doc.company = self.company
doc.title = "_Test Tax"
doc.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "Sales Expenses - _TC",
"description": "Test taxes",
"rate": 9,
},
)
doc.insert()
# Create a Sales Invoice
sinv = frappe.new_doc("Sales Invoice")
sinv.customer = self.customer
sinv.company = self.company
sinv.currency = "INR"
sinv.taxes_and_charges = "_Test Tax - _TC"
sinv.append("items", {"item_code": "_Test Item", "qty": 1, "rate": 50})
sinv.insert()
self.assertEqual(sinv.total_taxes_and_charges, 4.5)
def test_20_journal_against_sales_invoice(self):
# Invoice in Foreign Currency
si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1)
@@ -2146,3 +2175,59 @@ class TestAccountsController(FrappeTestCase):
si_1 = create_sales_invoice(do_not_submit=True)
si_1.items[0].project = project.name
self.assertRaises(frappe.ValidationError, si_1.save)
def test_party_billing_and_shipping_address(self):
from erpnext.crm.doctype.prospect.test_prospect import make_address
customer_billing = make_address(address_title="Customer")
customer_billing.append("links", {"link_doctype": "Customer", "link_name": "_Test Customer"})
customer_billing.save()
supplier_billing = make_address(address_title="Supplier", address_line1="2", city="Ahmedabad")
supplier_billing.append("links", {"link_doctype": "Supplier", "link_name": "_Test Supplier"})
supplier_billing.save()
customer_shipping = make_address(
address_title="Customer", address_type="Shipping", address_line1="10"
)
customer_shipping.append("links", {"link_doctype": "Customer", "link_name": "_Test Customer"})
customer_shipping.save()
supplier_shipping = make_address(
address_title="Supplier", address_type="Shipping", address_line1="20", city="Ahmedabad"
)
supplier_shipping.append("links", {"link_doctype": "Supplier", "link_name": "_Test Supplier"})
supplier_shipping.save()
si = create_sales_invoice(do_not_save=True)
si.customer_address = supplier_billing.name
self.assertRaises(frappe.ValidationError, si.save)
si.customer_address = customer_billing.name
si.save()
si.shipping_address_name = supplier_shipping.name
self.assertRaises(frappe.ValidationError, si.save)
si.shipping_address_name = customer_shipping.name
si.reload()
si.save()
pi = make_purchase_invoice(do_not_save=True)
pi.supplier_address = customer_shipping.name
self.assertRaises(frappe.ValidationError, pi.save)
pi.supplier_address = supplier_shipping.name
pi.save()
def test_party_contact(self):
from frappe.contacts.doctype.contact.test_contact import create_contact
customer_contact = create_contact(name="Customer", salutation="Mr", save=False)
customer_contact.append("links", {"link_doctype": "Customer", "link_name": "_Test Customer"})
customer_contact.save()
supplier_contact = create_contact(name="Supplier", salutation="Mr", save=False)
supplier_contact.append("links", {"link_doctype": "Supplier", "link_name": "_Test Supplier"})
supplier_contact.save()
si = create_sales_invoice(do_not_save=True)
si.contact_person = supplier_contact.name
self.assertRaises(frappe.ValidationError, si.save)
si.contact_person = customer_contact.name
si.save()

View File

@@ -1,6 +1,7 @@
{
"actions": [],
"allow_events_in_timeline": 1,
"allow_rename": 1,
"autoname": "field:company_name",
"creation": "2021-08-19 00:21:06.995448",
"doctype": "DocType",
@@ -216,9 +217,10 @@
"options": "CRM Note"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2022-10-13 12:29:33.674561",
"modified": "2025-03-06 11:31:49.083983",
"modified_by": "Administrator",
"module": "CRM",
"name": "Prospect",
@@ -258,9 +260,9 @@
"share": 1
}
],
"sort_field": "modified",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"title_field": "company_name",
"track_changes": 1
}
}

View File

@@ -4,7 +4,7 @@ app_publisher = "Frappe Technologies Pvt. Ltd."
app_description = """ERP made simple"""
app_icon = "fa fa-th"
app_color = "#e74c3c"
app_email = "info@erpnext.com"
app_email = "hello@frappe.io"
app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"
app_logo_url = "/assets/erpnext/images/erpnext-logo.svg"
@@ -484,7 +484,7 @@ email_brand_image = "assets/erpnext/images/erpnext-logo.jpg"
default_mail_footer = """
<span>
Sent via
<a class="text-muted" href="https://erpnext.com?source=via_email_footer" target="_blank">
<a class="text-muted" href="https://frappe.io/erpnext?source=via_email_footer" target="_blank">
ERPNext
</a>
</span>

View File

@@ -440,12 +440,12 @@ class BOM(WebsiteGenerator):
"description": item and args["description"] or "",
"image": item and args["image"] or "",
"stock_uom": item and args["stock_uom"] or "",
"uom": item and args["stock_uom"] or "",
"conversion_factor": 1,
"uom": args["uom"] if args.get("uom") else item and args["stock_uom"] or "",
"conversion_factor": args["conversion_factor"] if args.get("conversion_factor") else 1,
"bom_no": args["bom_no"],
"rate": rate,
"qty": args.get("qty") or args.get("stock_qty") or 1,
"stock_qty": args.get("qty") or args.get("stock_qty") or 1,
"stock_qty": args.get("stock_qty") or args.get("qty") or 1,
"base_rate": flt(rate) * (flt(self.conversion_rate) or 1),
"include_item_in_manufacturing": cint(args.get("transfer_for_manufacture")),
"sourced_by_supplier": args.get("sourced_by_supplier", 0),
@@ -1371,7 +1371,7 @@ def add_operations_cost(stock_entry, work_order=None, expense_account=None):
},
)
def get_max_op_qty():
def get_max_operation_quantity():
from frappe.query_builder.functions import Sum
table = frappe.qb.DocType("Job Card")
@@ -1387,7 +1387,7 @@ def add_operations_cost(stock_entry, work_order=None, expense_account=None):
)
return min([d.qty for d in query.run(as_dict=True)], default=0)
def get_utilised_cc():
def get_utilised_corrective_cost():
from frappe.query_builder.functions import Sum
table = frappe.qb.DocType("Stock Entry")
@@ -1417,15 +1417,15 @@ def add_operations_cost(stock_entry, work_order=None, expense_account=None):
)
)
):
max_qty = get_max_op_qty() - work_order.produced_qty
remaining_cc = work_order.corrective_operation_cost - get_utilised_cc()
max_qty = get_max_operation_quantity() - work_order.produced_qty
remaining_corrective_cost = work_order.corrective_operation_cost - get_utilised_corrective_cost()
stock_entry.append(
"additional_costs",
{
"expense_account": expense_account,
"description": "Corrective Operation Cost",
"has_corrective_cost": 1,
"amount": remaining_cc / max_qty * flt(stock_entry.fg_completed_qty),
"amount": remaining_corrective_cost / max_qty * flt(stock_entry.fg_completed_qty),
},
)

View File

@@ -28,6 +28,8 @@ BOM_ITEM_FIELDS = [
"stock_uom",
"conversion_factor",
"do_not_explode",
"source_warehouse",
"allow_alternative_item",
]
@@ -291,7 +293,6 @@ class BOMCreator(Document):
"item": row.item_code,
"bom_type": "Production",
"quantity": row.qty,
"allow_alternative_item": 1,
"bom_creator": self.name,
"bom_creator_item": bom_creator_item,
}
@@ -315,7 +316,6 @@ class BOMCreator(Document):
item_args.update(
{
"bom_no": bom_no,
"allow_alternative_item": 1,
"allow_scrap_items": 1,
"include_item_in_manufacturing": 1,
}
@@ -428,6 +428,7 @@ def add_sub_assembly(**kwargs):
"do_not_explode": 1,
"is_expandable": 1,
"stock_uom": item_info.stock_uom,
"allow_alternative_item": kwargs.allow_alternative_item,
},
)

View File

@@ -15,6 +15,7 @@
"is_expandable",
"sourced_by_supplier",
"bom_created",
"allow_alternative_item",
"description_section",
"description",
"quantity_and_rate_section",
@@ -225,12 +226,18 @@
"label": "BOM Created",
"no_copy": 1,
"print_hide": 1
},
{
"default": "1",
"fieldname": "allow_alternative_item",
"fieldtype": "Check",
"label": "Allow Alternative Item"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-06-03 18:45:24.339532",
"modified": "2025-02-19 13:25:15.732496",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Creator Item",

View File

@@ -14,6 +14,7 @@ class BOMCreatorItem(Document):
if TYPE_CHECKING:
from frappe.types import DF
allow_alternative_item: DF.Check
amount: DF.Currency
base_amount: DF.Currency
base_rate: DF.Currency

View File

@@ -312,7 +312,7 @@ frappe.ui.form.on("Job Card", {
fieldtype: "Float",
label: __("Completed Quantity"),
fieldname: "qty",
default: frm.doc.for_quantity,
default: frm.doc.for_quantity - frm.doc.total_completed_qty,
},
(data) => {
frm.events.complete_job(frm, "Complete", data.qty);

View File

@@ -86,7 +86,6 @@
"search_index": 1
},
{
"fetch_from": "work_order.bom_no",
"fieldname": "bom_no",
"fieldtype": "Link",
"label": "BOM No",
@@ -281,7 +280,7 @@
"fieldtype": "Column Break"
},
{
"fetch_from": "work_order.production_item",
"fetch_from": "bom_no.item",
"fieldname": "production_item",
"fieldtype": "Link",
"label": "Production Item",
@@ -511,7 +510,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2023-06-28 19:23:14.345214",
"modified": "2025-03-17 15:55:11.143456",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card",

View File

@@ -64,9 +64,7 @@ class JobCard(Document):
from erpnext.manufacturing.doctype.job_card_scheduled_time.job_card_scheduled_time import (
JobCardScheduledTime,
)
from erpnext.manufacturing.doctype.job_card_scrap_item.job_card_scrap_item import (
JobCardScrapItem,
)
from erpnext.manufacturing.doctype.job_card_scrap_item.job_card_scrap_item import JobCardScrapItem
from erpnext.manufacturing.doctype.job_card_time_log.job_card_time_log import JobCardTimeLog
actual_end_date: DF.Datetime | None
@@ -91,7 +89,7 @@ class JobCard(Document):
naming_series: DF.Literal["PO-JOB.#####"]
operation: DF.Link
operation_id: DF.Data | None
operation_row_number: DF.Literal
operation_row_number: DF.Literal[None]
posting_date: DF.Date | None
process_loss_qty: DF.Float
production_item: DF.Link | None

View File

@@ -363,8 +363,8 @@ class ProductionPlan(Document):
for item in items:
item.pending_qty = (
flt(item.qty) - max(item.work_order_qty, item.delivered_qty, 0) * item.conversion_factor
)
flt(item.qty) - max(item.work_order_qty, item.delivered_qty, 0)
) * item.conversion_factor
pi = frappe.qb.DocType("Packed Item")
@@ -425,6 +425,7 @@ class ProductionPlan(Document):
mr_item.item_code,
mr_item.warehouse,
mr_item.description,
mr_item.bom_no,
((mr_item.qty - mr_item.ordered_qty) * mr_item.conversion_factor).as_("pending_qty"),
)
.distinct()

View File

@@ -42,6 +42,7 @@ class TestWorkOrder(FrappeTestCase):
prepare_data_for_backflush_based_on_materials_transferred()
def tearDown(self):
frappe.local.future_sle = {}
frappe.db.rollback()
def check_planned_qty(self):

View File

@@ -301,6 +301,12 @@ frappe.ui.form.on("Work Order", {
label: __("Sequence Id"),
read_only: 1,
},
{
fieldtype: "Link",
fieldname: "bom",
label: __("BOM"),
read_only: 1,
},
],
data: operations_data,
in_place_edit: true,
@@ -341,6 +347,7 @@ frappe.ui.form.on("Work Order", {
qty: pending_qty,
pending_qty: pending_qty,
sequence_id: data.sequence_id,
bom: data.bom,
});
}
}

View File

@@ -164,7 +164,7 @@ class WorkOrder(Document):
if self.source_warehouse:
self.set_warehouses()
validate_uom_is_integer(self, "stock_uom", ["qty", "produced_qty"])
validate_uom_is_integer(self, "stock_uom", ["required_qty"])
self.set_required_items(reset_only_qty=len(self.get("required_items")))
@@ -1616,7 +1616,7 @@ def create_job_card(work_order, row, enable_capacity_planning=False, auto_create
"posting_date": nowdate(),
"for_quantity": row.job_card_qty or work_order.get("qty", 0),
"operation_id": row.get("name"),
"bom_no": work_order.bom_no,
"bom_no": row.get("bom"),
"project": work_order.project,
"company": work_order.company,
"sequence_id": row.get("sequence_id"),

View File

@@ -4,7 +4,7 @@
import frappe
from frappe import _, scrub
from frappe.utils import getdate
from frappe.utils import getdate, today
from erpnext.stock.report.stock_analytics.stock_analytics import get_period, get_period_date_ranges
@@ -30,11 +30,12 @@ def get_columns(filters):
def get_periodic_data(filters, entry):
periodic_data = {
"All Work Orders": {},
"Not Started": {},
"Overdue": {},
"Pending": {},
"Completed": {},
"Closed": {},
"Stopped": {},
}
ranges = get_period_date_ranges(filters)
@@ -42,33 +43,24 @@ def get_periodic_data(filters, entry):
for from_date, end_date in ranges:
period = get_period(end_date, filters)
for d in entry:
if getdate(d.creation) <= getdate(from_date) or getdate(d.creation) <= getdate(end_date):
periodic_data = update_periodic_data(periodic_data, "All Work Orders", period)
if d.status == "Completed":
if getdate(d.actual_end_date) < getdate(from_date) or getdate(d.modified) < getdate(
from_date
):
periodic_data = update_periodic_data(periodic_data, "Completed", period)
elif getdate(d.actual_start_date) < getdate(from_date):
periodic_data = update_periodic_data(periodic_data, "Pending", period)
elif getdate(d.planned_start_date) < getdate(from_date):
periodic_data = update_periodic_data(periodic_data, "Overdue", period)
else:
periodic_data = update_periodic_data(periodic_data, "Not Started", period)
if getdate(from_date) <= getdate(d.creation) <= getdate(end_date) and d.status not in [
"Draft",
"Submitted",
"Completed",
"Cancelled",
]:
if d.status in ["Not Started", "Closed", "Stopped"]:
periodic_data = update_periodic_data(periodic_data, d.status, period)
elif getdate(today()) > getdate(d.planned_end_date):
periodic_data = update_periodic_data(periodic_data, "Overdue", period)
elif getdate(today()) < getdate(d.planned_end_date):
periodic_data = update_periodic_data(periodic_data, "Pending", period)
elif d.status == "In Process":
if getdate(d.actual_start_date) < getdate(from_date):
periodic_data = update_periodic_data(periodic_data, "Pending", period)
elif getdate(d.planned_start_date) < getdate(from_date):
periodic_data = update_periodic_data(periodic_data, "Overdue", period)
else:
periodic_data = update_periodic_data(periodic_data, "Not Started", period)
elif d.status == "Not Started":
if getdate(d.planned_start_date) < getdate(from_date):
periodic_data = update_periodic_data(periodic_data, "Overdue", period)
else:
periodic_data = update_periodic_data(periodic_data, "Not Started", period)
if (
getdate(from_date) <= getdate(d.actual_end_date) <= getdate(end_date)
and d.status == "Completed"
):
periodic_data = update_periodic_data(periodic_data, "Completed", period)
return periodic_data
@@ -88,10 +80,7 @@ def get_data(filters, columns):
"Work Order",
fields=[
"creation",
"modified",
"actual_start_date",
"actual_end_date",
"planned_start_date",
"planned_end_date",
"status",
],
@@ -100,7 +89,7 @@ def get_data(filters, columns):
periodic_data = get_periodic_data(filters, entry)
labels = ["All Work Orders", "Not Started", "Overdue", "Pending", "Completed"]
labels = ["Not Started", "Overdue", "Pending", "Completed", "Closed", "Stopped"]
chart_data = get_chart_data(periodic_data, columns)
ranges = get_period_date_ranges(filters)
@@ -121,21 +110,23 @@ def get_data(filters, columns):
def get_chart_data(periodic_data, columns):
labels = [d.get("label") for d in columns[1:]]
all_data, not_start, overdue, pending, completed = [], [], [], [], []
not_start, overdue, pending, completed, closed, stopped = [], [], [], [], [], []
datasets = []
for d in labels:
all_data.append(periodic_data.get("All Work Orders").get(d))
not_start.append(periodic_data.get("Not Started").get(d))
overdue.append(periodic_data.get("Overdue").get(d))
pending.append(periodic_data.get("Pending").get(d))
completed.append(periodic_data.get("Completed").get(d))
closed.append(periodic_data.get("Closed").get(d))
stopped.append(periodic_data.get("Stopped").get(d))
datasets.append({"name": _("All Work Orders"), "values": all_data})
datasets.append({"name": _("Not Started"), "values": not_start})
datasets.append({"name": _("Overdue"), "values": overdue})
datasets.append({"name": _("Pending"), "values": pending})
datasets.append({"name": _("Completed"), "values": completed})
datasets.append({"name": _("Closed"), "values": closed})
datasets.append({"name": _("Stopped"), "values": stopped})
chart = {"data": {"labels": labels, "datasets": datasets}}
chart["type"] = "line"

View File

@@ -261,6 +261,7 @@ erpnext.patches.v14_0.show_loan_management_deprecation_warning
erpnext.patches.v14_0.clear_reconciliation_values_from_singles
execute:frappe.rename_doc("Report", "TDS Payable Monthly", "Tax Withholding Details", force=True)
erpnext.patches.v14_0.update_proprietorship_to_individual
erpnext.patches.v15_0.rename_subcontracting_fields
[post_model_sync]
erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets
@@ -394,3 +395,9 @@ execute:frappe.db.set_single_value("Accounts Settings", "exchange_gain_loss_post
erpnext.patches.v14_0.disable_add_row_in_gross_profit
erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment
erpnext.patches.v14_0.update_posting_datetime
erpnext.stock.doctype.stock_ledger_entry.patches.ensure_sle_indexes
erpnext.patches.v15_0.update_query_report
erpnext.patches.v15_0.rename_field_from_rate_difference_to_amount_difference #2025-03-18
erpnext.patches.v15_0.recalculate_amount_difference_field
erpnext.patches.v15_0.rename_sla_fields #2025-03-12
erpnext.patches.v15_0.set_purchase_receipt_row_item_to_capitalization_stock_item

View File

@@ -82,6 +82,9 @@ def get_asset_depreciation_schedules_map():
.orderby(ds.idx)
).run(as_dict=True)
if len(records) > 20000:
frappe.db.auto_commit_on_many_writes = True
asset_depreciation_schedules_map = frappe._dict()
for d in records:
asset_depreciation_schedules_map.setdefault((d.asset_name, cstr(d.finance_book)), []).append(d)

View File

@@ -0,0 +1,126 @@
import frappe
from frappe.query_builder.functions import Sum
from frappe.utils import flt, getdate
from erpnext.accounts.utils import get_fiscal_year
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import adjust_incoming_rate_for_pr
def execute():
if not frappe.db.get_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate"):
return
for company in frappe.get_all("Company", pluck="name"):
table = frappe.qb.DocType("Purchase Receipt Item")
parent = frappe.qb.DocType("Purchase Receipt")
query = (
frappe.qb.from_(table)
.join(parent)
.on(table.parent == parent.name)
.select(
table.parent,
table.name,
table.amount,
table.billed_amt,
table.amount_difference_with_purchase_invoice,
table.rate,
table.qty,
parent.conversion_rate,
)
.where((table.docstatus == 1) & (parent.company == company))
)
posting_date = "2024-04-01"
# Get the last accounting period end date
accounting_period = frappe.get_all(
"Accounting Period", {"company": company}, ["end_date"], order_by="end_date desc", limit=1
)
if (
accounting_period
and accounting_period[0].end_date
and getdate(accounting_period[0].end_date) > getdate(posting_date)
):
posting_date = accounting_period[0].end_date
# Get the last period closing voucher end date
period_closing_voucher = frappe.get_all(
"Period Closing Voucher",
{"company": company, "docstatus": 1},
["period_end_date"],
order_by="period_end_date desc",
limit=1,
)
if (
period_closing_voucher
and period_closing_voucher[0].period_end_date
and getdate(period_closing_voucher[0].period_end_date) > getdate(posting_date)
):
posting_date = period_closing_voucher[0].period_end_date
acc_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
if acc_frozen_upto and getdate(acc_frozen_upto) > getdate(posting_date):
posting_date = acc_frozen_upto
stock_frozen_upto = frappe.db.get_single_value("Stock Settings", "stock_frozen_upto")
if stock_frozen_upto and getdate(stock_frozen_upto) > getdate(posting_date):
posting_date = stock_frozen_upto
try:
fiscal_year = get_fiscal_year(frappe.utils.datetime.date.today())
except Exception:
return
else:
if fiscal_year and getdate(fiscal_year[1]) > getdate(posting_date):
posting_date = fiscal_year[1]
query = query.where(parent.posting_date > posting_date)
if result := query.run(as_dict=True):
item_wise_billed_qty = get_billed_qty_against_purchase_receipt([item.name for item in result])
purchase_receipts = set()
precision = frappe.get_precision("Purchase Receipt Item", "amount")
for item in result:
adjusted_amt = 0.0
if (
item.billed_amt is not None
and item.amount is not None
and item_wise_billed_qty.get(item.name)
):
adjusted_amt = (
flt(item.billed_amt / item_wise_billed_qty.get(item.name)) - flt(item.rate)
) * item.qty
adjusted_amt = flt(
adjusted_amt * flt(item.conversion_rate),
precision,
)
if adjusted_amt != item.amount_difference_with_purchase_invoice:
frappe.db.set_value(
"Purchase Receipt Item",
item.name,
"amount_difference_with_purchase_invoice",
adjusted_amt,
update_modified=False,
)
purchase_receipts.add(item.parent)
for pr in purchase_receipts:
adjust_incoming_rate_for_pr(frappe.get_doc("Purchase Receipt", pr))
def get_billed_qty_against_purchase_receipt(pr_names):
table = frappe.qb.DocType("Purchase Invoice Item")
query = (
frappe.qb.from_(table)
.select(table.pr_detail, Sum(table.qty).as_("qty"))
.where((table.pr_detail.isin(pr_names)) & (table.docstatus == 1))
.groupby(table.pr_detail)
)
invoice_data = query.run(as_list=1)
if not invoice_data:
return frappe._dict()
return frappe._dict(invoice_data)

View File

@@ -0,0 +1,17 @@
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
frappe.db.set_value(
"DocField",
{"parent": "Purchase Receipt Item", "fieldname": "rate_difference_with_purchase_invoice"},
"label",
"Amount Difference with Purchase Invoice",
)
rename_field(
"Purchase Receipt Item",
"rate_difference_with_purchase_invoice",
"amount_difference_with_purchase_invoice",
)
frappe.clear_cache(doctype="Purchase Receipt Item")

View File

@@ -0,0 +1,23 @@
import frappe
from frappe.custom.doctype.custom_field.custom_field import rename_fieldname
from frappe.model.utils.rename_field import rename_field
def execute():
doctypes = frappe.get_all("Service Level Agreement", pluck="document_type", distinct=True)
for doctype in doctypes:
if doctype == "Issue":
continue
if frappe.db.exists(
"Custom Field", {"name": doctype + "-resolution_by", "fieldname": "resolution_by"}
):
rename_fieldname(doctype + "-resolution_by", "sla_resolution_by")
if frappe.db.exists(
"Custom Field", {"name": doctype + "-resolution_date", "fieldname": "resolution_date"}
):
rename_fieldname(doctype + "-resolution_date", "sla_resolution_date")
rename_field("Issue", "resolution_by", "sla_resolution_by")
rename_field("Issue", "resolution_date", "sla_resolution_date")

View File

@@ -0,0 +1,14 @@
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
if frappe.db.table_exists("Purchase Order Item") and frappe.db.has_column(
"Purchase Order Item", "sco_qty"
):
rename_field("Purchase Order Item", "sco_qty", "subcontracted_quantity")
if frappe.db.table_exists("Subcontracting Order Item") and frappe.db.has_column(
"Subcontracting Order Item", "sc_conversion_factor"
):
rename_field("Subcontracting Order Item", "sc_conversion_factor", "subcontracting_conversion_factor")

View File

@@ -0,0 +1,21 @@
import frappe
def execute():
# nosemgrep
frappe.db.sql(
"""
UPDATE `tabAsset Capitalization Stock Item` ACSI
JOIN `tabAsset Capitalization` AC
ON ACSI.parent = AC.name
JOIN `tabPurchase Receipt Item` PRI
ON
PRI.item_code = ACSI.item_code
AND PRI.wip_composite_asset = AC.target_asset
SET
ACSI.purchase_receipt_item = PRI.name
WHERE
ACSI.purchase_receipt_item IS NULL
AND AC.docstatus = 1
"""
)

View File

@@ -0,0 +1,25 @@
import frappe
def execute():
reports = [
"Accounts Payable",
"Accounts Payable Summary",
"Purchase Register",
"Item-wise Purchase Register",
"Purchase Order Analysis",
"Received Items To Be Billed",
"Supplier Ledger Summary",
]
frappe.db.set_value(
"Workspace Link",
{
"parent": "Payables",
"link_type": "Report",
"type": "Link",
"link_to": ["in", reports],
"is_query_report": 0,
},
"is_query_report",
1,
)

View File

@@ -322,17 +322,31 @@ class Project(Document):
self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0
def update_billed_amount(self):
# nosemgrep
self.total_billed_amount = self.get_billed_amount_from_parent() + self.get_billed_amount_from_child()
def get_billed_amount_from_parent(self):
total_billed_amount = frappe.db.sql(
"""select sum(base_net_amount)
from `tabSales Invoice Item` si_item, `tabSales Invoice` si
where si_item.parent = si.name
and if(si_item.project, si_item.project, si.project) = %s
and si.docstatus=1""",
from `tabSales Invoice` si join `tabSales Invoice Item` si_item on si_item.parent = si.name
where si_item.project is null
and si.project is not null
and si.project = %s
and si.docstatus = 1""",
self.name,
)
self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0
return total_billed_amount and total_billed_amount[0][0] or 0
def get_billed_amount_from_child(self):
total_billed_amount = frappe.db.sql(
"""select sum(base_net_amount)
from `tabSales Invoice Item`
where project = %s
and docstatus = 1""",
self.name,
)
return total_billed_amount and total_billed_amount[0][0] or 0
def after_rename(self, old_name, new_name, merge=False):
if old_name == self.copied_from:

View File

@@ -1,189 +1,69 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-02-21 16:42:13.882879",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"creation": "2018-02-21 16:42:13.882879",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"timesheet_sb",
"ignore_workstation_time_overlap",
"ignore_user_time_overlap",
"ignore_employee_time_overlap",
"fetch_timesheet_in_sales_invoice"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "timesheet_sb",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Timesheets",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"fieldname": "timesheet_sb",
"fieldtype": "Section Break",
"label": "Timesheets"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "ignore_workstation_time_overlap",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Ignore Workstation Time Overlap",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"fieldname": "ignore_workstation_time_overlap",
"fieldtype": "Check",
"label": "Ignore Workstation Time Overlap"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "ignore_user_time_overlap",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Ignore User Time Overlap",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
"default": "0",
"fieldname": "ignore_user_time_overlap",
"fieldtype": "Check",
"label": "Ignore User Time Overlap"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "ignore_employee_time_overlap",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Ignore Employee Time Overlap",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"default": "0",
"fieldname": "ignore_employee_time_overlap",
"fieldtype": "Check",
"label": "Ignore Employee Time Overlap"
},
{
"default": "0",
"fieldname": "fetch_timesheet_in_sales_invoice",
"fieldtype": "Check",
"label": "Fetch Timesheet in Sales Invoice"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2018-02-21 16:42:42.357209",
"modified_by": "Administrator",
"module": "Projects",
"name": "Projects Settings",
"name_case": "",
"owner": "Administrator",
],
"issingle": 1,
"links": [],
"modified": "2025-02-25 22:48:03.164862",
"modified_by": "Administrator",
"module": "Projects",
"name": "Projects Settings",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -14,6 +14,7 @@ class ProjectsSettings(Document):
if TYPE_CHECKING:
from frappe.types import DF
fetch_timesheet_in_sales_invoice: DF.Check
ignore_employee_time_overlap: DF.Check
ignore_user_time_overlap: DF.Check
ignore_workstation_time_overlap: DF.Check

View File

@@ -5,6 +5,7 @@ import datetime
import unittest
import frappe
from frappe.tests.utils import change_settings
from frappe.utils import add_to_date, now_datetime, nowdate
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
@@ -53,6 +54,7 @@ class TestTimesheet(unittest.TestCase):
self.assertEqual(item.qty, 2.00)
self.assertEqual(item.rate, 50.00)
@change_settings("Projects Settings", {"fetch_timesheet_in_sales_invoice": 1})
def test_timesheet_billing_based_on_project(self):
emp = make_employee("test_employee_6@salary.com")
project = frappe.get_value("Project", {"project_name": "_Test Project"})
@@ -62,6 +64,7 @@ class TestTimesheet(unittest.TestCase):
)
sales_invoice = create_sales_invoice(do_not_save=True)
sales_invoice.project = project
sales_invoice.add_timesheet_data()
sales_invoice.submit()
ts = frappe.get_doc("Timesheet", timesheet.name)

View File

@@ -210,6 +210,13 @@ class BOMConfigurator {
[
{ label: __("Item"), fieldname: "item_code", fieldtype: "Link", options: "Item", reqd: 1 },
{ label: __("Qty"), fieldname: "qty", default: 1.0, fieldtype: "Float", reqd: 1 },
{
label: __("Allow Alternative Item"),
fieldname: "allow_alternative_item",
default: 1.0,
fieldtype: "Check",
reqd: 1,
},
],
(data) => {
if (!node.data.parent_id) {
@@ -224,6 +231,7 @@ class BOMConfigurator {
item_code: data.item_code,
fg_reference_id: node.data.name || this.frm.doc.name,
qty: data.qty,
allow_alternative_item: data.allow_alternative_item,
},
callback: (r) => {
view.events.load_tree(r, node);
@@ -258,6 +266,7 @@ class BOMConfigurator {
fg_item: node.data.value,
fg_reference_id: node.data.name || this.frm.doc.name,
bom_item: bom_item,
allow_alternative_item: bom_item.allow_alternative_item,
},
callback: (r) => {
view.events.load_tree(r, node);
@@ -278,6 +287,14 @@ class BOMConfigurator {
reqd: 1,
read_only: read_only,
},
{
label: __("Allow Alternative Item"),
fieldname: "allow_alternative_item",
default: 1.0,
fieldtype: "Check",
reqd: 1,
read_only: read_only,
},
{ fieldtype: "Column Break" },
{
label: __("Qty"),

View File

@@ -194,7 +194,7 @@ erpnext.buying = {
}
qty(doc, cdt, cdn) {
if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && (doc.update_stock || doc.is_return))) {
if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && doc.update_stock)) {
this.calculate_received_qty(doc, cdt, cdn)
}
super.qty(doc, cdt, cdn);

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