Compare commits

..

61 Commits

Author SHA1 Message Date
Frappe PR Bot
616448f9c9 chore(release): Bumped to Version 14.92.2
## [14.92.2](https://github.com/frappe/erpnext/compare/v14.92.1...v14.92.2) (2025-10-21)

### Bug Fixes

* do reposting of first transfer entry based on item-wh combination ([85c509e](85c509ea69))
* handle flt conversion for prev_ordered_qty ([9460568](94605687c7))
2025-10-21 12:45:22 +00:00
Diptanil Saha
70a8c64ea4 Merge pull request #50181 from frappe/version-14-hotfix
chore: release v14
2025-10-21 18:13:40 +05:30
mergify[bot]
00a75d4984 refactor: simplify expression (backport #50168) (#50169)
Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2025-10-19 23:59:31 +02:00
rohitwaghchaure
8b826c2369 Merge pull request #50121 from frappe/mergify/bp/version-14-hotfix/pr-50061
fix: do reposting of first transfer entry based on item-wh combination (backport #50061)
2025-10-16 15:02:52 +05:30
rohitwaghchaure
210786119d chore: fix conflicts 2025-10-16 00:17:50 +05:30
Rohit Waghchaure
85c509ea69 fix: do reposting of first transfer entry based on item-wh combination
(cherry picked from commit 2f25b445ab)

# Conflicts:
#	erpnext/stock/stock_ledger.py
2025-10-15 18:44:46 +00:00
Mihir Kandoi
6e35b6b6b3 Merge pull request #50105 from frappe/mergify/bp/version-14-hotfix/pr-50078
fix: handle flt conversion for prev_ordered_qty (backport #50078)
2025-10-15 00:15:14 +05:30
Kavin
94605687c7 fix: handle flt conversion for prev_ordered_qty
(cherry picked from commit 77c35ef47f)
2025-10-14 18:10:27 +00:00
Frappe PR Bot
e27518b2d4 chore(release): Bumped to Version 14.92.1
## [14.92.1](https://github.com/frappe/erpnext/compare/v14.92.0...v14.92.1) (2025-10-14)

### Bug Fixes

* sanitize projects field in tasks webform ([#50089](https://github.com/frappe/erpnext/issues/50089)) ([aadc83d](aadc83d0d9))
2025-10-14 14:18:29 +00:00
Diptanil Saha
cf71178af6 Merge pull request #50099 from frappe/version-14-hotfix
chore: release v14
2025-10-14 19:46:52 +05:30
ruthra kumar
002575244f Merge pull request #50097 from frappe/mergify/bp/version-14-hotfix/pr-50089
fix: sanitize projects field in tasks webform (backport #50089)
2025-10-14 19:22:52 +05:30
Akhil Narang
aadc83d0d9 fix: sanitize projects field in tasks webform (#50089)
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
(cherry picked from commit f8b50d3ffa)
2025-10-14 13:08:25 +00:00
Frappe PR Bot
66f1e2a076 chore(release): Bumped to Version 14.92.0
# [14.92.0](https://github.com/frappe/erpnext/compare/v14.91.3...v14.92.0) (2025-10-07)

### Bug Fixes

* **Common Code:** fetch canonical URI from Code List (backport [#49882](https://github.com/frappe/erpnext/issues/49882)) ([#49883](https://github.com/frappe/erpnext/issues/49883)) ([9b5183e](9b5183e0e5))
* do not fetch disabled item tax template ([dcae024](dcae024fd2))
* linter; dont change doc after DB update (backport [#49907](https://github.com/frappe/erpnext/issues/49907)) ([#49909](https://github.com/frappe/erpnext/issues/49909)) ([30a6fe5](30a6fe55ca))
* resolved conflict ([2c383a6](2c383a69f9))
* Set paid amount automatically only if return entry validated and has negative grand total ([#49829](https://github.com/frappe/erpnext/issues/49829)) ([b7d76d8](b7d76d8fad))

### Features

* dynamic due date in payment terms when fetched from order (backport [#48864](https://github.com/frappe/erpnext/issues/48864)) ([#49937](https://github.com/frappe/erpnext/issues/49937)) ([d82106c](d82106cb76))
2025-10-07 13:18:52 +00:00
ruthra kumar
1d5935a10f chore: release v14 (#49943)
* refactor(Supplier): custom buttons call make methods (backport #49840) (#49841)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

* fix(Common Code): fetch canonical URI from Code List (backport #49882) (#49883)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Common Code): fetch canonical URI from Code List (#49882)

* fix: Set paid amount automatically only if return entry validated and has negative grand total (#49829)

(cherry picked from commit dcbcc596f2)

# Conflicts:
#	erpnext/public/js/controllers/taxes_and_totals.js

* fix: resolved conflict

* fix: linter; dont change doc after DB update (backport #49907) (#49909)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
fix: linter; dont change doc after DB update (#49907)

* fix: do not fetch disabled item tax template

(cherry picked from commit b10cf4a928)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
#	erpnext/stock/get_item_details.py

* chore: fix conflicts

* chore: fix conflicts

* chore: fix linters issue

* feat: dynamic due date in payment terms when fetched from order (backport #48864) (#49937)

* feat: dynamic due date in payment terms when fetched from order (#48864)

* fix: dynamic due date when payment terms are fetched from order

* fix(test): use change_settings decorator for settings enable and disable

* fix(test): compare schedule for due_date dynamically

* fix: save conditions for due date at invoice level

* fix: make fields read only and on change of date unset the date condition fields

* fix: remove fetch_form

* fix: correct field assingment

* fix: revert unwanted changes

* refactor: streamline payment term field assignments and enhance discount date handling

* refactor: remove payment_term from fields_to_copy and optimize currency handling in transaction callback

* refactor: ensure default values for payment schedule and discount validity fields

(cherry picked from commit 3c70cbbaf8)

# Conflicts:
#	erpnext/accounts/doctype/payment_schedule/payment_schedule.json
#	erpnext/accounts/doctype/payment_schedule/payment_schedule.py
#	erpnext/public/js/controllers/transaction.js
#	erpnext/selling/doctype/sales_order/test_sales_order.py
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py

* chore: resolve conflicts

---------

Co-authored-by: Lakshit Jain <ljain112@gmail.com>

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
Co-authored-by: ljain112 <ljain112@gmail.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2025-10-07 18:47:23 +05:30
mergify[bot]
d82106cb76 feat: dynamic due date in payment terms when fetched from order (backport #48864) (#49937)
* feat: dynamic due date in payment terms when fetched from order (#48864)

* fix: dynamic due date when payment terms are fetched from order

* fix(test): use change_settings decorator for settings enable and disable

* fix(test): compare schedule for due_date dynamically

* fix: save conditions for due date at invoice level

* fix: make fields read only and on change of date unset the date condition fields

* fix: remove fetch_form

* fix: correct field assingment

* fix: revert unwanted changes

* refactor: streamline payment term field assignments and enhance discount date handling

* refactor: remove payment_term from fields_to_copy and optimize currency handling in transaction callback

* refactor: ensure default values for payment schedule and discount validity fields

(cherry picked from commit 3c70cbbaf8)

# Conflicts:
#	erpnext/accounts/doctype/payment_schedule/payment_schedule.json
#	erpnext/accounts/doctype/payment_schedule/payment_schedule.py
#	erpnext/public/js/controllers/transaction.js
#	erpnext/selling/doctype/sales_order/test_sales_order.py
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py

* chore: resolve conflicts

---------

Co-authored-by: Lakshit Jain <ljain112@gmail.com>
2025-10-07 14:10:46 +05:30
rohitwaghchaure
03aeac6c9b Merge pull request #49931 from frappe/mergify/bp/version-14-hotfix/pr-49702
fix: do not fetch disabled item tax template (backport #49702)
2025-10-07 11:54:19 +05:30
rohitwaghchaure
014f5bff5c chore: fix linters issue 2025-10-07 11:24:49 +05:30
rohitwaghchaure
819667ab24 chore: fix conflicts 2025-10-07 11:00:45 +05:30
rohitwaghchaure
aa20e224fb chore: fix conflicts 2025-10-07 11:00:02 +05:30
ljain112
dcae024fd2 fix: do not fetch disabled item tax template
(cherry picked from commit b10cf4a928)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
#	erpnext/stock/get_item_details.py
2025-10-07 05:26:09 +00:00
mergify[bot]
30a6fe55ca fix: linter; dont change doc after DB update (backport #49907) (#49909)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
fix: linter; dont change doc after DB update (#49907)
2025-10-06 13:34:24 +02:00
ruthra kumar
57f9894f41 Merge pull request #49892 from frappe/mergify/bp/version-14-hotfix/pr-49829
fix: Set paid amount automatically only if return entry validated and has negative grand total (backport #49829)
2025-10-06 12:58:55 +05:30
Nabin Hait
2c383a69f9 fix: resolved conflict 2025-10-06 12:19:22 +05:30
Nabin Hait
b7d76d8fad fix: Set paid amount automatically only if return entry validated and has negative grand total (#49829)
(cherry picked from commit dcbcc596f2)

# Conflicts:
#	erpnext/public/js/controllers/taxes_and_totals.js
2025-10-06 05:58:16 +00:00
mergify[bot]
9b5183e0e5 fix(Common Code): fetch canonical URI from Code List (backport #49882) (#49883)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Common Code): fetch canonical URI from Code List (#49882)
2025-10-04 19:22:04 +02:00
mergify[bot]
0b43d518af refactor(Supplier): custom buttons call make methods (backport #49840) (#49841)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-10-02 00:16:20 +02:00
Frappe PR Bot
a6b490e4b3 chore(release): Bumped to Version 14.91.3
## [14.91.3](https://github.com/frappe/erpnext/compare/v14.91.2...v14.91.3) (2025-09-30)

### Performance Improvements

* reposting for backdated transactions ([3d51c1b](3d51c1b363))
2025-09-30 13:07:25 +00:00
ruthra kumar
a5d33d5665 Merge pull request #49802 from frappe/version-14-hotfix
chore: release v14
2025-09-30 18:36:03 +05:30
Frappe PR Bot
48c3c80383 chore(release): Bumped to Version 14.91.2
## [14.91.2](https://github.com/frappe/erpnext/compare/v14.91.1...v14.91.2) (2025-09-25)

### Performance Improvements

* reposting for backdated transactions ([f3da431](f3da431f1f))
2025-09-25 14:45:52 +00:00
rohitwaghchaure
a9a68f2523 Merge pull request #49726 from frappe/mergify/bp/version-14/pr-49723
perf: reposting for backdated transactions (backport #49720) (backport #49723)
2025-09-25 20:14:23 +05:30
Rohit Waghchaure
f3da431f1f perf: reposting for backdated transactions
(cherry picked from commit 1b0fc0541b)
(cherry picked from commit 3d51c1b363)
2025-09-25 13:22:15 +00:00
rohitwaghchaure
14e92d589f Merge pull request #49723 from frappe/mergify/bp/version-14-hotfix/pr-49720
perf: reposting for backdated transactions (backport #49720)
2025-09-25 18:50:18 +05:30
Rohit Waghchaure
3d51c1b363 perf: reposting for backdated transactions
(cherry picked from commit 1b0fc0541b)
2025-09-25 12:16:23 +00:00
Frappe PR Bot
825ef415ef chore(release): Bumped to Version 14.91.1
## [14.91.1](https://github.com/frappe/erpnext/compare/v14.91.0...v14.91.1) (2025-09-23)

### Bug Fixes

* add condition for name ([0c7911d](0c7911d0fe))
2025-09-23 13:35:10 +00:00
ruthra kumar
f6148c4352 chore: release v14 (#49696)
* fix: add condition for name

(cherry picked from commit cf5a2d6351)

* test: add test to validate user permission in qb

(cherry picked from commit a5b881ea74)

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

---------

Co-authored-by: venkat102 <venkatesharunachalam659@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2025-09-23 19:03:40 +05:30
ruthra kumar
3c408ff66b Merge pull request #49616 from frappe/mergify/bp/version-14-hotfix/pr-49467
fix: add condition for name (backport #49467)
2025-09-22 10:49:01 +05:30
venkat102
f6bc9fc505 test: add test to validate user permission in qb
(cherry picked from commit a5b881ea74)

# Conflicts:
#	erpnext/setup/doctype/employee/test_employee.py
2025-09-18 16:47:10 +05:30
venkat102
0c7911d0fe fix: add condition for name
(cherry picked from commit cf5a2d6351)
2025-09-18 11:12:13 +00:00
Frappe PR Bot
b8ecc4274f chore(release): Bumped to Version 14.91.0
# [14.91.0](https://github.com/frappe/erpnext/compare/v14.90.1...v14.91.0) (2025-09-16)

### Bug Fixes

* duplicate items being created when fetching items from warehouse in stock reco ([646de0b](646de0bec1))
* remove ignore_permissions ([180f406](180f406917))

### Features

* add permission check for custom button ([42b38b7](42b38b7998))
2025-09-16 14:41:37 +00:00
ruthra kumar
a9b7bc1385 Merge pull request #49565 from frappe/version-14-hotfix
chore: release v14
2025-09-16 20:08:58 +05:30
Mihir Kandoi
d062e50f35 Merge pull request #49515 from frappe/mergify/bp/version-14-hotfix/pr-48456 2025-09-10 10:06:48 +05:30
Mihir Kandoi
646de0bec1 fix: duplicate items being created when fetching items from warehouse in stock reco
(cherry picked from commit 73f6c29559)
2025-09-10 03:01:06 +00:00
Raffael Meyer
02de47c673 Merge pull request #49434 from frappe/mergify/bp/version-14-hotfix/pr-49374
fix: improve permission handling for party link creation (backport #49374)
2025-09-03 17:12:27 +02:00
Marc-Constantin Enke
42b38b7998 feat: add permission check for custom button
(cherry picked from commit 00fd1d2f26)
2025-09-02 17:20:31 +00:00
Marc-Constantin Enke
180f406917 fix: remove ignore_permissions
(cherry picked from commit 7f55f421ab)
2025-09-02 17:20:31 +00:00
Frappe PR Bot
90803b852e chore(release): Bumped to Version 14.90.1
## [14.90.1](https://github.com/frappe/erpnext/compare/v14.90.0...v14.90.1) (2025-09-02)

### Bug Fixes

* add is_cancelled in condition ([b9f9be3](b9f9be3d88))
* ignore cancelled gl and add company filter ([6b29c06](6b29c06511))
* set missing due date in Purchase Invoice and POS Invoice ([#49232](https://github.com/frappe/erpnext/issues/49232)) ([2b6b0b3](2b6b0b32a7))
2025-09-02 13:48:57 +00:00
ruthra kumar
4cbde47ac4 Merge pull request #49424 from frappe/version-14-hotfix
chore: release v14
2025-09-02 19:17:38 +05:30
ruthra kumar
0b3215cd42 Merge pull request #49416 from frappe/mergify/bp/version-14-hotfix/pr-49379
fix: add is_cancelled in condition (backport #49379)
2025-09-02 11:16:44 +05:30
l0gesh29
b9f9be3d88 fix: add is_cancelled in condition
(cherry picked from commit 77a9cf6398)
2025-09-02 05:27:23 +00:00
ruthra kumar
faeedb01a8 Merge pull request #49411 from frappe/mergify/bp/version-14-hotfix/pr-49366
fix(trial-balance-simple): ignore cancelled gl and add company filter (backport #49366)
2025-09-02 10:42:36 +05:30
Raffael Meyer
a2402ddf52 Merge pull request #49233 from frappe/mergify/bp/version-14-hotfix/pr-49232
fix: set missing due date in Purchase Invoice and POS Invoice (backport #49232)
2025-09-02 00:49:37 +02:00
l0gesh29
6b29c06511 fix: ignore cancelled gl and add company filter
(cherry picked from commit afb067ce50)
2025-09-01 11:16:22 +00:00
Frappe PR Bot
2cedd455d9 chore(release): Bumped to Version 14.90.0
# [14.90.0](https://github.com/frappe/erpnext/compare/v14.89.2...v14.90.0) (2025-08-26)

### Bug Fixes

* **dunning:** include accounting dimension upon gl creation ([4fccef0](4fccef0636))
* **payment_entry:** clear party_type when switching to Internal Transfer to avoid 'Supplier None not found' ([e27dd64](e27dd64044))
* **Supplier:** add make_method for Pricing Rule (backport [#49282](https://github.com/frappe/erpnext/issues/49282)) ([#49283](https://github.com/frappe/erpnext/issues/49283)) ([7986e69](7986e69839))

### Features

* add make methods for Bank Account (backport [#49000](https://github.com/frappe/erpnext/issues/49000)) ([#49274](https://github.com/frappe/erpnext/issues/49274)) ([836545b](836545bdb4))
2025-08-26 11:35:26 +00:00
ruthra kumar
ba31f685f8 Merge pull request #49327 from frappe/version-14-hotfix
chore: release v14
2025-08-26 17:01:27 +05:30
ruthra kumar
507d27d509 Merge pull request #49309 from niyati34/fix/payment-entry-clear-party-type-v14hotfix
Fix/payment entry clear party type v14hotfix
2025-08-26 13:27:38 +05:30
niyati34
e27dd64044 fix(payment_entry): clear party_type when switching to Internal Transfer to avoid 'Supplier None not found' 2025-08-25 19:49:05 +05:30
ruthra kumar
7f617a54d1 Merge pull request #49278 from aerele/dunning-accouting-dimension
fix(dunning): include accounting dimension upon gl creation
2025-08-25 16:56:35 +05:30
mergify[bot]
7986e69839 fix(Supplier): add make_method for Pricing Rule (backport #49282) (#49283)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
fix(Supplier): add make_method for Pricing Rule (#49282)
2025-08-22 19:26:10 +02:00
mergify[bot]
836545bdb4 feat: add make methods for Bank Account (backport #49000) (#49274)
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-08-22 19:17:44 +02:00
ravibharathi656
4fccef0636 fix(dunning): include accounting dimension upon gl creation 2025-08-22 12:34:39 +05:30
Raffael Meyer
2b6b0b32a7 fix: set missing due date in Purchase Invoice and POS Invoice (#49232)
(cherry picked from commit 77478303fe)
2025-08-19 12:30:46 +00:00
32 changed files with 313 additions and 187 deletions

View File

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

View File

@@ -55,46 +55,46 @@ class Dunning(AccountsController):
"conversion_rate",
"cost_center",
]
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
accounting_dimensions = get_accounting_dimensions()
invoice_fields.extend(accounting_dimensions)
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate)
default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
gl_entries.append(
self.get_gl_dict(
{
"account": inv.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against": self.income_account,
"debit": dunning_in_company_currency,
"debit_in_account_currency": self.dunning_amount,
"against_voucher": self.name,
"against_voucher_type": "Dunning",
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
},
inv.party_account_currency,
item=inv,
)
)
gl_entries.append(
self.get_gl_dict(
{
"account": self.income_account,
"against": self.customer,
"credit": dunning_in_company_currency,
"cost_center": inv.cost_center or default_cost_center,
"credit_in_account_currency": self.dunning_amount,
"project": inv.project,
},
item=inv,
)
)
debit = {
"account": inv.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against": self.income_account,
"debit": dunning_in_company_currency,
"debit_in_account_currency": self.dunning_amount,
"against_voucher": self.name,
"against_voucher_type": "Dunning",
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
}
credit = {
"account": self.income_account,
"against": self.customer,
"credit": dunning_in_company_currency,
"credit_in_account_currency": self.dunning_amount,
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
}
for dimension in accounting_dimensions:
if val := inv.get(dimension):
debit[dimension] = credit[dimension] = val
gl_entries = [
self.get_gl_dict(debit, inv.party_account_currency, item=inv),
self.get_gl_dict(credit, item=inv),
]
make_gl_entries(
gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False
)

View File

@@ -261,7 +261,7 @@ def validate_balance_type(account, adv_adj=False):
if balance_must_be:
balance = frappe.db.sql(
"""select sum(debit) - sum(credit)
from `tabGL Entry` where account = %s""",
from `tabGL Entry` where is_cancelled = 0 and account = %s""",
account,
)[0][0]

View File

@@ -60,6 +60,6 @@ def create_party_link(primary_role, primary_party, secondary_party):
party_link.secondary_role = "Customer" if primary_role == "Supplier" else "Supplier"
party_link.secondary_party = secondary_party
party_link.save(ignore_permissions=True)
party_link.save()
return party_link

View File

@@ -330,7 +330,7 @@ frappe.ui.form.on('Payment Entry', {
payment_type: function(frm) {
set_default_party_type(frm);
if(frm.doc.payment_type == "Internal Transfer") {
$.each(["party", "party_balance", "paid_from", "paid_to",
$.each(["party", "party_type", "party_balance", "paid_from", "paid_to",
"references", "total_allocated_amount"], function(i, field) {
frm.set_value(field, null);
});

View File

@@ -10,14 +10,19 @@
"description",
"section_break_4",
"due_date",
"invoice_portion",
"mode_of_payment",
"column_break_5",
"invoice_portion",
"due_date_based_on",
"credit_days",
"credit_months",
"section_break_6",
"discount_type",
"discount_date",
"column_break_9",
"discount",
"discount_type",
"column_break_9",
"discount_validity_based_on",
"discount_validity",
"section_break_9",
"payment_amount",
"outstanding",
@@ -155,12 +160,50 @@
"fieldtype": "Currency",
"label": "Payment Amount (Company Currency)",
"options": "Company:company:default_currency"
},
{
"fieldname": "due_date_based_on",
"fieldtype": "Select",
"label": "Due Date Based On",
"options": "\nDay(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
"read_only": 1
},
{
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
"fieldname": "credit_days",
"fieldtype": "Int",
"label": "Credit Days",
"non_negative": 1,
"read_only": 1
},
{
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
"fieldname": "credit_months",
"fieldtype": "Int",
"label": "Credit Months",
"non_negative": 1,
"read_only": 1
},
{
"depends_on": "discount",
"fieldname": "discount_validity_based_on",
"fieldtype": "Select",
"label": "Discount Validity Based On",
"options": "\nDay(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
"read_only": 1
},
{
"depends_on": "discount_validity_based_on",
"fieldname": "discount_validity",
"fieldtype": "Int",
"label": "Discount Validity",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-09-16 13:57:06.382859",
"modified": "2025-07-31 08:38:25.820701",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Schedule",
@@ -171,4 +214,4 @@
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -161,4 +161,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@@ -560,7 +560,13 @@ class POSInvoice(SalesInvoice):
"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(SalesInvoice, self).set_missing_values(for_validate)

View File

@@ -173,7 +173,12 @@ class PurchaseInvoice(BuyingController):
)
if not self.due_date:
self.due_date = get_due_date(
self.posting_date, "Supplier", self.supplier, self.company, self.bill_date
self.posting_date,
"Supplier",
self.supplier,
self.company,
self.bill_date,
template_name=self.payment_terms_template,
)
tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")

View File

@@ -1816,19 +1816,16 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
self.assertAlmostEqual(rate, 500)
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
def test_payment_allocation_for_payment_terms(self):
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
create_pr_against_po,
create_purchase_order,
)
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
)
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
make_purchase_invoice as make_pi_from_pr,
)
automatically_fetch_payment_terms()
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",
@@ -1854,7 +1851,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
pi = make_pi_from_pr(pr.name)
self.assertEqual(pi.payment_schedule[0].payment_amount, 1000)
automatically_fetch_payment_terms(enable=0)
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",

View File

@@ -4,15 +4,25 @@
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"mandatory": 1,
"options": "Company",
"wildcard_filter": 0
}
],
"idx": 0,
"is_standard": "Yes",
"modified": "2019-01-17 17:20:42.374958",
"modified": "2025-08-28 19:06:54.273322",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Trial Balance (Simple)",
"owner": "Administrator",
"prepared_report": 0,
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\nwhere is_cancelled = 0 and company = %(company)s\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
"ref_doctype": "GL Entry",
"report_name": "Trial Balance (Simple)",
"report_type": "Query Report",

View File

@@ -2219,6 +2219,10 @@ def build_qb_match_conditions(doctype, user=None) -> list:
for filter in match_filters:
for link_option, allowed_values in filter.items():
fieldnames = link_fields_map.get(link_option, [])
cond = None
if link_option == doctype:
cond = _dt["name"].isin(allowed_values)
for fieldname in fieldnames:
field = _dt[fieldname]
@@ -2227,6 +2231,7 @@ def build_qb_match_conditions(doctype, user=None) -> list:
if not apply_strict_user_permissions:
cond = (Coalesce(field, "") == "") | cond
if cond:
criterion.append(cond)
return criterion

View File

@@ -456,9 +456,8 @@ class PurchaseOrder(BuyingController):
if not self.is_against_so():
return
for item in removed_items:
prev_ordered_qty = (
prev_ordered_qty = flt(
frappe.get_cached_value("Sales Order Item", item.get("sales_order_item"), "ordered_qty")
or 0.0
)
frappe.db.set_value(

View File

@@ -526,12 +526,8 @@ class TestPurchaseOrder(FrappeTestCase):
self.assertRaises(frappe.ValidationError, pr.submit)
self.assertRaises(frappe.ValidationError, pi.submit)
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
def test_make_purchase_invoice_with_terms(self):
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
)
automatically_fetch_payment_terms()
po = create_purchase_order(do_not_save=True)
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
@@ -555,7 +551,6 @@ class TestPurchaseOrder(FrappeTestCase):
self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
automatically_fetch_payment_terms(enable=0)
def test_warehouse_company_validation(self):
from erpnext.stock.utils import InvalidWarehouseCompany
@@ -703,6 +698,7 @@ class TestPurchaseOrder(FrappeTestCase):
)
self.assertEqual(due_date, "2023-03-31")
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 0})
def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self):
po = create_purchase_order(do_not_save=1)
po.payment_terms_template = "_Test Payment Term Template"
@@ -834,18 +830,16 @@ class TestPurchaseOrder(FrappeTestCase):
bo.load_from_db()
self.assertEqual(bo.items[0].ordered_qty, 5)
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
create_payment_terms_template,
)
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
compare_payment_schedules,
)
automatically_fetch_payment_terms()
po = create_purchase_order(qty=10, rate=100, do_not_save=1)
create_payment_terms_template()
po.payment_terms_template = "Test Receivable Template"
@@ -859,8 +853,6 @@ class TestPurchaseOrder(FrappeTestCase):
# self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
compare_payment_schedules(self, po, pi)
automatically_fetch_payment_terms(enable=0)
def test_internal_transfer_flow(self):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
make_inter_company_purchase_invoice,

View File

@@ -42,6 +42,11 @@ frappe.ui.form.on("Supplier", {
},
};
});
frm.make_methods = {
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
"Pricing Rule": () => erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name),
};
},
refresh: function (frm) {
@@ -84,21 +89,9 @@ frappe.ui.form.on("Supplier", {
__("View")
);
frm.add_custom_button(
__("Bank Account"),
function () {
erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name);
},
__("Create")
);
frm.add_custom_button(__("Bank Account"), () => frm.make_methods["Bank Account"](), __("Create"));
frm.add_custom_button(
__("Pricing Rule"),
function () {
erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
},
__("Create")
);
frm.add_custom_button(__("Pricing Rule"), () => frm.make_methods["Pricing Rule"](), __("Create"));
frm.add_custom_button(
__("Get Supplier Group Details"),
@@ -108,7 +101,10 @@ frappe.ui.form.on("Supplier", {
__("Actions")
);
if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) {
if (
cint(frappe.defaults.get_default("enable_common_party_accounting")) &&
frappe.model.can_create("Party Link")
) {
frm.add_custom_button(
__("Link with Customer"),
function () {

View File

@@ -213,6 +213,11 @@ class AccountsController(TransactionBase):
self.validate_date_with_fiscal_year()
self.validate_party_accounts()
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
if self.is_return:
self.validate_qty()
else:
self.validate_deferred_start_and_end_date()
self.validate_inter_company_reference()
@@ -258,11 +263,6 @@ class AccountsController(TransactionBase):
self.set_advance_gain_or_loss()
if self.is_return:
self.validate_qty()
else:
self.validate_deferred_start_and_end_date()
self.validate_deferred_income_expense_account()
self.set_inter_company_account()
@@ -2239,6 +2239,7 @@ class AccountsController(TransactionBase):
self.payment_schedule = []
self.payment_terms_template = po_or_so.payment_terms_template
posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date")
for schedule in po_or_so.payment_schedule:
payment_schedule = {
@@ -2251,6 +2252,17 @@ class AccountsController(TransactionBase):
}
if automatically_fetch_payment_terms:
if schedule.due_date_based_on:
payment_schedule["due_date"] = get_due_date(schedule, posting_date)
payment_schedule["due_date_based_on"] = schedule.due_date_based_on
payment_schedule["credit_days"] = cint(schedule.credit_days)
payment_schedule["credit_months"] = cint(schedule.credit_months)
if schedule.discount_validity_based_on:
payment_schedule["discount_date"] = get_discount_date(schedule, posting_date)
payment_schedule["discount_validity_based_on"] = schedule.discount_validity_based_on
payment_schedule["discount_validity"] = cint(schedule.discount_validity)
payment_schedule["payment_amount"] = flt(
grand_total * flt(payment_schedule["invoice_portion"]) / 100,
schedule.precision("payment_amount"),
@@ -2738,9 +2750,7 @@ def set_balance_in_account_currency(
_("Account: {0} with currency: {1} can not be selected").format(gl_dict.account, account_currency)
)
gl_dict["account_currency"] = (
company_currency if account_currency == company_currency else account_currency
)
gl_dict["account_currency"] = account_currency
# set debit/credit in account currency if not provided
if flt(gl_dict.debit) and not flt(gl_dict.debit_in_account_currency):
@@ -2987,14 +2997,26 @@ def get_payment_term_details(
term = frappe.get_doc("Payment Term", term)
else:
term_details.payment_term = term.payment_term
term_details.description = term.description
term_details.invoice_portion = term.invoice_portion
fields_to_copy = [
"description",
"invoice_portion",
"discount_type",
"discount",
"mode_of_payment",
"due_date_based_on",
"credit_days",
"credit_months",
"discount_validity_based_on",
"discount_validity",
]
for field in fields_to_copy:
term_details[field] = term.get(field)
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
term_details.base_payment_amount = flt(term.invoice_portion) * flt(base_grand_total) / 100
term_details.discount_type = term.discount_type
term_details.discount = term.discount
term_details.outstanding = term_details.payment_amount
term_details.mode_of_payment = term.mode_of_payment
if bill_date:
term_details.due_date = get_due_date(term, bill_date)
@@ -3013,11 +3035,11 @@ def get_due_date(term, posting_date=None, bill_date=None):
due_date = None
date = bill_date or posting_date
if term.due_date_based_on == "Day(s) after invoice date":
due_date = add_days(date, term.credit_days)
due_date = add_days(date, cint(term.credit_days))
elif term.due_date_based_on == "Day(s) after the end of the invoice month":
due_date = add_days(get_last_day(date), term.credit_days)
due_date = add_days(get_last_day(date), cint(term.credit_days))
elif term.due_date_based_on == "Month(s) after the end of the invoice month":
due_date = get_last_day(add_months(date, term.credit_months))
due_date = get_last_day(add_months(date, cint(term.credit_months)))
return due_date
@@ -3025,11 +3047,11 @@ def get_discount_date(term, posting_date=None, bill_date=None):
discount_validity = None
date = bill_date or posting_date
if term.discount_validity_based_on == "Day(s) after invoice date":
discount_validity = add_days(date, term.discount_validity)
discount_validity = add_days(date, cint(term.discount_validity))
elif term.discount_validity_based_on == "Day(s) after the end of the invoice month":
discount_validity = add_days(get_last_day(date), term.discount_validity)
discount_validity = add_days(get_last_day(date), cint(term.discount_validity))
elif term.discount_validity_based_on == "Month(s) after the end of the invoice month":
discount_validity = get_last_day(add_months(date, term.discount_validity))
discount_validity = get_last_day(add_months(date, cint(term.discount_validity)))
return discount_validity

View File

@@ -6,6 +6,7 @@
"engine": "InnoDB",
"field_order": [
"code_list",
"canonical_uri",
"title",
"common_code",
"description",
@@ -71,10 +72,17 @@
"in_list_view": 1,
"label": "Description",
"max_height": "60px"
},
{
"fetch_from": "code_list.canonical_uri",
"fieldname": "canonical_uri",
"fieldtype": "Data",
"label": "Canonical URI"
}
],
"grid_page_length": 50,
"links": [],
"modified": "2024-11-06 07:46:17.175687",
"modified": "2025-10-04 17:22:28.176155",
"modified_by": "Administrator",
"module": "EDI",
"name": "Common Code",
@@ -94,10 +102,11 @@
"write": 1
}
],
"row_format": "Dynamic",
"search_fields": "common_code,description",
"show_title_field_in_link": 1,
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"title_field": "title"
}
}

View File

@@ -22,6 +22,7 @@ class CommonCode(Document):
additional_data: DF.Code | None
applies_to: DF.Table[DynamicLink]
canonical_uri: DF.Data | None
code_list: DF.Link
common_code: DF.Data
description: DF.SmallText | None

View File

@@ -1,15 +1,17 @@
import urllib.parse
import frappe
def get_context(context):
if frappe.form_dict.project:
context.parents = [
{"title": frappe.form_dict.project, "route": "/projects?project=" + frappe.form_dict.project}
]
context.success_url = "/projects?project=" + frappe.form_dict.project
if project := frappe.form_dict.project:
title = frappe.utils.data.escape_html(project)
route = "/projects?" + urllib.parse.urlencode({"project": project})
context.parents = [{"title": title, "route": route}]
context.success_url = route
elif context.doc and context.doc.get("project"):
context.parents = [
{"title": context.doc.project, "route": "/projects?project=" + context.doc.project}
]
context.success_url = "/projects?project=" + context.doc.project
elif context.doc and (project := context.doc.get("project")):
title = frappe.utils.data.escape_html(project)
route = "/projects?" + urllib.parse.urlencode({"project": project})
context.parents = [{"title": title, "route": route}]
context.success_url = route

View File

@@ -76,9 +76,10 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
// Update paid amount on return/debit note creation
if (
this.frm.doc.doctype === "Purchase Invoice"
&& this.frm.doc.is_return
&& (this.frm.doc.grand_total > this.frm.doc.paid_amount)
this.frm.doc.doctype === "Purchase Invoice" &&
this.frm.doc.is_return &&
this.frm.doc.grand_total < 0 &&
this.frm.doc.grand_total > this.frm.doc.paid_amount
) {
this.frm.doc.paid_amount = flt(this.frm.doc.grand_total, precision("grand_total"));
}

View File

@@ -878,6 +878,15 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}
}
discount_date(doc, cdt, cdn) {
// Remove fields as discount_date is auto-managed by payment terms
const row = locals[cdt][cdn];
["discount_validity", "discount_validity_based_on"].forEach((field) => {
row[field] = "";
});
this.frm.refresh_field("payment_schedule");
}
due_date() {
// due_date is to be changed, payment terms template and/or payment schedule must
// be removed as due_date is automatically changed based on payment terms
@@ -2203,6 +2212,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
'item_code': item.item_code,
'valid_from': ["<=", doc.transaction_date || doc.bill_date || doc.posting_date],
'item_group': item.item_group,
'disabled': 0,
}
if (doc.tax_category)
@@ -2244,7 +2254,18 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
payment_term(doc, cdt, cdn) {
const me = this;
var row = locals[cdt][cdn];
if(row.payment_term) {
// empty date condition fields
[
"due_date_based_on",
"credit_days",
"credit_months",
"discount_validity",
"discount_validity_based_on",
].forEach(function (field) {
row[field] = "";
});
if (row.payment_term) {
frappe.call({
method: "erpnext.controllers.accounts_controller.get_payment_term_details",
args: {
@@ -2254,16 +2275,19 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
grand_total: this.frm.doc.rounded_total || this.frm.doc.grand_total,
base_grand_total: this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total
},
callback: function(r) {
if(r.message && !r.exc) {
for (var d in r.message) {
frappe.model.set_value(cdt, cdn, d, r.message[d]);
const company_currency = me.get_company_currency();
me.update_payment_schedule_grid_labels(company_currency);
callback: function (r) {
if (r.message && !r.exc) {
const company_currency = me.get_company_currency();
for (let d in r.message) {
row[d] = r.message[d];
}
me.update_payment_schedule_grid_labels(company_currency);
me.frm.refresh_field("payment_schedule");
}
}
})
},
});
} else {
me.frm.refresh_field("payment_schedule");
}
}

View File

@@ -215,17 +215,9 @@ $.extend(erpnext.utils, {
},
make_bank_account: function (doctype, docname) {
frappe.call({
method: "erpnext.accounts.doctype.bank_account.bank_account.make_bank_account",
args: {
doctype: doctype,
docname: docname,
},
freeze: true,
callback: function (r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
},
frappe.new_doc("Bank Account", {
party_type: doctype,
party: docname,
});
},

View File

@@ -14,6 +14,7 @@ frappe.ui.form.on("Customer", {
method: "erpnext.selling.doctype.customer.customer.make_opportunity",
frm: cur_frm,
}),
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
};
frm.add_fetch("lead_name", "company_name", "customer_name");
@@ -155,7 +156,10 @@ frappe.ui.form.on("Customer", {
__("Actions")
);
if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) {
if (
cint(frappe.defaults.get_default("enable_common_party_accounting")) &&
frappe.model.can_create("Party Link")
) {
frm.add_custom_button(
__("Link with Supplier"),
function () {

View File

@@ -10,7 +10,7 @@ from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, getdate, nowdate, today
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.controllers.accounts_controller import update_child_qty_rate
from erpnext.controllers.accounts_controller import get_due_date, update_child_qty_rate
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
make_maintenance_schedule,
)
@@ -1759,14 +1759,13 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
so.load_from_db()
self.assertRaises(frappe.LinkExistsError, so.cancel)
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
create_payment_terms_template,
)
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
automatically_fetch_payment_terms()
so = make_sales_order(uom="Nos", do_not_save=1)
create_payment_terms_template()
so.payment_terms_template = "Test Receivable Template"
@@ -1780,8 +1779,6 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
self.assertEqual(so.payment_terms_template, si.payment_terms_template)
compare_payment_schedules(self, so, si)
automatically_fetch_payment_terms(enable=0)
def test_zero_amount_sales_order_billing_status(self):
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
@@ -2284,16 +2281,14 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
self.assertRaises(frappe.ValidationError, so1.update_status, "Draft")
def automatically_fetch_payment_terms(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.automatically_fetch_payment_terms = enable
accounts_settings.save()
def compare_payment_schedules(doc, doc1, doc2):
for index, schedule in enumerate(doc1.get("payment_schedule")):
posting_date = doc1.get("bill_date") or doc1.get("posting_date") or doc1.get("transaction_date")
due_date = schedule.due_date
if schedule.due_date_based_on:
due_date = get_due_date(schedule, posting_date=posting_date)
doc.assertEqual(schedule.payment_term, doc2.payment_schedule[index].payment_term)
doc.assertEqual(getdate(schedule.due_date), doc2.payment_schedule[index].due_date)
doc.assertEqual(due_date, doc2.payment_schedule[index].due_date)
doc.assertEqual(schedule.invoice_portion, doc2.payment_schedule[index].invoice_portion)
doc.assertEqual(schedule.payment_amount, doc2.payment_schedule[index].payment_amount)

View File

@@ -37,15 +37,15 @@ class SellingSettings(Document):
)
def toggle_hide_tax_id(self):
self.hide_tax_id = cint(self.hide_tax_id)
_hide_tax_id = cint(self.hide_tax_id)
# Make property setters to hide tax_id fields
for doctype in ("Sales Order", "Sales Invoice", "Delivery Note"):
make_property_setter(
doctype, "tax_id", "hidden", self.hide_tax_id, "Check", validate_fields_for_doctype=False
doctype, "tax_id", "hidden", _hide_tax_id, "Check", validate_fields_for_doctype=False
)
make_property_setter(
doctype, "tax_id", "print_hide", self.hide_tax_id, "Check", validate_fields_for_doctype=False
doctype, "tax_id", "print_hide", _hide_tax_id, "Check", validate_fields_for_doctype=False
)
def toggle_editable_rate_for_bundle_items(self):

View File

@@ -21,6 +21,12 @@ erpnext.setup.EmployeeController = class EmployeeController extends frappe.ui.fo
};
frappe.ui.form.on("Employee", {
setup: function (frm) {
frm.make_methods = {
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
};
},
onload: function (frm) {
frm.set_query("department", function () {
return {

View File

@@ -5,8 +5,10 @@ import unittest
import frappe
import frappe.utils
from frappe.query_builder import Criterion
import erpnext
from erpnext.accounts.utils import build_qb_match_conditions
from erpnext.setup.doctype.employee.employee import InactiveEmployeeStatusError
test_records = frappe.get_test_records("Employee")
@@ -34,6 +36,32 @@ class TestEmployee(unittest.TestCase):
employee_doc.save()
self.assertTrue("Employee" not in frappe.get_roles(user))
def test_employee_user_permission(self):
employee1 = make_employee("employee_1_test@company.com", create_user_permission=1)
employee2 = make_employee("employee_2_test@company.com", create_user_permission=1)
make_employee("employee_3_test@company.com", create_user_permission=1)
employee1_doc = frappe.get_doc("Employee", employee1)
employee2_doc = frappe.get_doc("Employee", employee2)
employee2_doc.reload()
employee2_doc.reports_to = employee1_doc.name
employee2_doc.save()
frappe.set_user(employee1_doc.user_id)
Employee = frappe.qb.DocType("Employee")
qb_employee_list = (
frappe.qb.from_(Employee)
.select(Employee.name)
.where(Criterion.all(build_qb_match_conditions("Employee")))
.orderby(Employee.Name)
).run(pluck=Employee.name)
employee_list = frappe.db.get_list("Employee", pluck="name", order_by="name")
self.assertEqual(qb_employee_list, employee_list)
frappe.set_user("Administrator")
def tearDown(self):
frappe.db.rollback()

View File

@@ -13,7 +13,6 @@ from erpnext.accounts.utils import get_balance_on
from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
compare_payment_schedules,
create_dn_against_so,
make_sales_order,
@@ -1026,14 +1025,13 @@ class TestDeliveryNote(FrappeTestCase):
self.assertTrue("TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item")
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
create_payment_terms_template,
)
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
automatically_fetch_payment_terms()
so = make_sales_order(uom="Nos", do_not_save=1)
create_payment_terms_template()
so.payment_terms_template = "Test Receivable Template"
@@ -1053,8 +1051,6 @@ class TestDeliveryNote(FrappeTestCase):
self.assertEqual(so.payment_terms_template, si.payment_terms_template)
compare_payment_schedules(self, so, si)
automatically_fetch_payment_terms(enable=0)
def test_returned_qty_in_return_dn(self):
# SO ---> SI ---> DN
# |

View File

@@ -1056,6 +1056,7 @@ class TestPurchaseReceipt(FrappeTestCase):
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
create_payment_terms_template,
@@ -1066,12 +1067,9 @@ class TestPurchaseReceipt(FrappeTestCase):
make_pr_against_po,
)
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
compare_payment_schedules,
)
automatically_fetch_payment_terms()
po = create_purchase_order(qty=10, rate=100, do_not_save=1)
create_payment_terms_template()
po.payment_terms_template = "Test Receivable Template"
@@ -1089,8 +1087,6 @@ class TestPurchaseReceipt(FrappeTestCase):
# self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
compare_payment_schedules(self, po, pi)
automatically_fetch_payment_terms(enable=0)
@change_settings("Stock Settings", {"allow_negative_stock": 1})
def test_neg_to_positive(self):
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry

View File

@@ -748,12 +748,12 @@ def get_items(warehouse, posting_date, posting_time, company, item_code=None, ig
itemwise_batch_data = get_itemwise_batch(warehouse, posting_date, company, item_code)
for d in items:
if d.item_code in itemwise_batch_data:
if (d.item_code, d.warehouse) in itemwise_batch_data:
valuation_rate = get_stock_balance(
d.item_code, d.warehouse, posting_date, posting_time, with_valuation_rate=True
)[1]
for row in itemwise_batch_data.get(d.item_code):
for row in itemwise_batch_data.get((d.item_code, d.warehouse)):
if ignore_empty_stock and not row.qty:
continue
@@ -885,7 +885,7 @@ def get_itemwise_batch(warehouse, posting_date, company, item_code=None):
columns, data = execute(filters)
for row in data:
itemwise_batch_data.setdefault(row[0], []).append(
itemwise_batch_data.setdefault((row[0], row[3]), []).append(
frappe._dict(
{
"item_code": row[0],

View File

@@ -635,8 +635,10 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False):
taxes_with_no_validity = []
for tax in taxes:
tax_company = frappe.get_cached_value("Item Tax Template", tax.item_tax_template, "company")
if tax_company == args["company"]:
disabled, tax_company = frappe.get_cached_value(
"Item Tax Template", tax.item_tax_template, ["disabled", "company"]
)
if not disabled and tax_company == args["company"]:
if tax.valid_from or tax.maximum_net_rate:
# In purchase Invoice first preference will be given to supplier invoice date
# if supplier date is not present then posting date

View File

@@ -489,32 +489,19 @@ class update_entries_after:
self.distinct_item_warehouses[key] = val
self.new_items_found = True
else:
# Check if the dependent voucher is reposted
# If not, then do not add it to the list
if not self.is_dependent_voucher_reposted(dependant_sle):
return
existing_sle_posting_date = self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key)
if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date):
if dependent_voucher_detail_nos and dependant_sle.voucher_detail_no in set(
dependent_voucher_detail_nos
):
existing_sle = self.distinct_item_warehouses[key].get("sle", {})
if getdate(existing_sle.get("posting_date")) > getdate(dependant_sle.posting_date):
self.distinct_item_warehouses[key] = val
self.new_items_found = True
elif dependant_sle.voucher_type == "Stock Entry" and is_transfer_stock_entry(
dependant_sle.voucher_no
):
if self.distinct_item_warehouses[key].get("transfer_entry_to_repost"):
return
val.sle_changed = True
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
val["transfer_entry_to_repost"] = True
self.distinct_item_warehouses[key] = val
self.new_items_found = True
elif dependant_sle.voucher_detail_no not in set(dependent_voucher_detail_nos):
# Future dependent voucher needs to be repost to get the correct stock value
# If dependent voucher has not reposted, then add it to the list
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
self.new_items_found = True
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
self.distinct_item_warehouses[key] = val
def is_dependent_voucher_reposted(self, dependant_sle) -> bool:
# Return False if the dependent voucher is not reposted
@@ -1403,6 +1390,8 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None):
"posting_time",
"voucher_detail_no",
"posting_datetime as timestamp",
"voucher_type",
"voucher_no",
],
as_dict=1,
)
@@ -1830,3 +1819,10 @@ def get_stock_value_difference(item_code, warehouse, posting_date, posting_time,
difference_amount = query.run()
return flt(difference_amount[0][0]) if difference_amount else 0
@frappe.request_cache
def is_transfer_stock_entry(voucher_no):
purpose = frappe.get_cached_value("Stock Entry", voucher_no, "purpose")
return purpose in ["Material Transfer", "Material Transfer for Manufacture", "Send to Subcontractor"]