Compare commits

..

47 Commits

Author SHA1 Message Date
Frappe PR Bot
05d5dc5321 chore(release): Bumped to Version 15.80.0
# [15.80.0](https://github.com/frappe/erpnext/compare/v15.79.2...v15.80.0) (2025-09-23)

### Bug Fixes

* **accounts receivable:** exclude employee transactions ([c071107](c071107b27))
* **accounts:** dynamic zero cutoff (backport [#48899](https://github.com/frappe/erpnext/issues/48899)) ([#49552](https://github.com/frappe/erpnext/issues/49552)) ([5ef7a78](5ef7a7857c))
* add condition for name ([65ce737](65ce737c2d))
* add option for currency field (backport [#49572](https://github.com/frappe/erpnext/issues/49572)) ([#49594](https://github.com/frappe/erpnext/issues/49594)) ([1a4ea07](1a4ea07b78))
* auto commit if too many writes reached ([0310172](0310172338))
* Consider non SABB batch qty in reserved batch validation ([7ed97e4](7ed97e47cc))
* incorrect current qty calculation for the batch ([bf48cf0](bf48cf02e5))
* **item:** avoid inheriting item defaults from identically named items ([#49571](https://github.com/frappe/erpnext/issues/49571)) ([9e58a56](9e58a56b5c))
* only show filters in print view if 'Include filters' is enabled ([79d4731](79d4731fe7))
* **payment-reconciliation:** apply field precision for allocated and difference amounts (backport [#49448](https://github.com/frappe/erpnext/issues/49448)) ([#49595](https://github.com/frappe/erpnext/issues/49595)) ([fbd62e7](fbd62e72f9))
* project accounting dimension and gl posting date column width (backport [#49563](https://github.com/frappe/erpnext/issues/49563)) ([#49591](https://github.com/frappe/erpnext/issues/49591)) ([bce22cb](bce22cb27b))
* **purchase order:** get party type based on supplier field ([3aebbf6](3aebbf6e92))
* restore auto_commit_on_many_writes flag ([5320782](53207821aa))
* set first_response_time on status change of issue ([0d5613c](0d5613ca4c))
* skip receivable/payable account validation in payroll entry if party is not available (backport [#49585](https://github.com/frappe/erpnext/issues/49585)) ([#49598](https://github.com/frappe/erpnext/issues/49598)) ([697f6ef](697f6ef086))
* **stock:** NoneType object error on stock entry ([de8c4ee](de8c4eec1b))
* warehouse for batch validation ([cb94c4b](cb94c4b5f1))

### Features

* add filters option in print view (backport [#49631](https://github.com/frappe/erpnext/issues/49631)) ([#49700](https://github.com/frappe/erpnext/issues/49700)) ([e9b29ed](e9b29ed544))
* populate available qty in pick list locations ([bc7f884](bc7f884ae1))
2025-09-23 14:00:33 +00:00
ruthra kumar
74b19f2743 Merge pull request #49697 from frappe/version-15-hotfix
chore: release v15
2025-09-23 19:29:11 +05:30
ruthra kumar
e9b29ed544 feat: add filters option in print view (backport #49631) (#49700)
fix: only show filters in print view if 'Include filters' is enabled

(cherry picked from commit 1979879b07)

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

Co-authored-by: Nareshkanna S <nareshkannashanmugam@gmail.com>
2025-09-23 16:40:11 +05:30
Nareshkanna S
79d4731fe7 fix: only show filters in print view if 'Include filters' is enabled
(cherry picked from commit 1979879b07)

# Conflicts:
#	erpnext/accounts/report/general_ledger/general_ledger.html
2025-09-23 16:29:18 +05:30
ruthra kumar
e4997af1c0 Merge pull request #49695 from frappe/mergify/bp/version-15-hotfix/pr-49694
chore: update sales_data from site_info (backport #49694)
2025-09-23 15:11:22 +05:30
Jannat Patel
ff08ee6ada test: activation with site_info
(cherry picked from commit 5a26d593e4)
2025-09-23 09:22:03 +00:00
Jannat Patel
ac55028f6b chore: update sales_data from site_info
(cherry picked from commit 866b252309)
2025-09-23 09:22:03 +00:00
Frappe PR Bot
98457a8530 chore(release): Bumped to Version 15.79.2
## [15.79.2](https://github.com/frappe/erpnext/compare/v15.79.1...v15.79.2) (2025-09-23)

### Bug Fixes

* Consider non SABB batch qty in reserved batch validation ([9158751](91587517d9))
* warehouse for batch validation ([59a7104](59a710433a))
2025-09-23 07:16:11 +00:00
rohitwaghchaure
236d8c7af1 Merge pull request #49693 from frappe/mergify/bp/version-15/pr-49691
fix: Consider non SABB batch qty in reserved batch validation (backport #49648) (backport #49691)
2025-09-23 12:44:43 +05:30
rohitwaghchaure
63c1fd52b1 Merge pull request #49692 from frappe/mergify/bp/version-15/pr-49688
fix: warehouse for batch validation (backport #49687) (backport #49688)
2025-09-23 12:44:21 +05:30
Kavin
91587517d9 fix: Consider non SABB batch qty in reserved batch validation
(cherry picked from commit ae8b34e03c)
(cherry picked from commit 7ed97e47cc)
2025-09-23 06:39:33 +00:00
rohitwaghchaure
7402549b4b Merge pull request #49691 from frappe/mergify/bp/version-15-hotfix/pr-49648
fix: Consider non SABB batch qty in reserved batch validation (backport #49648)
2025-09-23 12:09:11 +05:30
Rohit Waghchaure
59a710433a fix: warehouse for batch validation
(cherry picked from commit 381072170a)
(cherry picked from commit cb94c4b5f1)
2025-09-23 06:37:26 +00:00
rohitwaghchaure
f4cef8a417 Merge pull request #49690 from frappe/mergify/bp/version-15-hotfix/pr-49684
feat: populate available qty in pick list locations (backport #49684)
2025-09-23 12:07:00 +05:30
rohitwaghchaure
dc6d4f2620 Merge pull request #49688 from frappe/mergify/bp/version-15-hotfix/pr-49687
fix: warehouse for batch validation (backport #49687)
2025-09-23 12:06:31 +05:30
Kavin
7ed97e47cc fix: Consider non SABB batch qty in reserved batch validation
(cherry picked from commit ae8b34e03c)
2025-09-23 06:21:00 +00:00
Kavin
a463f4a419 refactor: fetching qty on warehouse trigger
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
(cherry picked from commit e3ab0e7c67)
2025-09-23 06:20:23 +00:00
Kavin
bc7f884ae1 feat: populate available qty in pick list locations
(cherry picked from commit d8756fc7de)
2025-09-23 06:20:23 +00:00
Kavin
056d51937b chore: rename stock qty label
(cherry picked from commit fc967fceb2)
2025-09-23 06:20:22 +00:00
Rohit Waghchaure
cb94c4b5f1 fix: warehouse for batch validation
(cherry picked from commit 381072170a)
2025-09-23 06:12:44 +00:00
Diptanil Saha
868987e46e Merge pull request #49686 from frappe/mergify/bp/version-15-hotfix/pr-49676
fix: auto commit if too many writes reached (backport #49676)
2025-09-23 11:20:36 +05:30
Kavin
53207821aa fix: restore auto_commit_on_many_writes flag
(cherry picked from commit 66712fa8b5)
2025-09-23 05:31:28 +00:00
Kavin
0310172338 fix: auto commit if too many writes reached
(cherry picked from commit 99a0ba0b45)
2025-09-23 05:31:27 +00:00
ruthra kumar
5bfb4d2390 Merge pull request #49667 from frappe/mergify/bp/version-15-hotfix/pr-49644
fix(purchase order): get party type based on supplier field (backport #49644)
2025-09-22 15:05:53 +05:30
venkat102
3aebbf6e92 fix(purchase order): get party type based on supplier field
(cherry picked from commit 11b9b1adc5)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
2025-09-22 15:01:57 +05:30
ruthra kumar
925afe1b59 Merge pull request #49665 from frappe/mergify/bp/version-15-hotfix/pr-49653
fix(accounts receivable): exclude employee transactions (backport #49653)
2025-09-22 14:58:17 +05:30
venkat102
c071107b27 fix(accounts receivable): exclude employee transactions
(cherry picked from commit 736a776d3d)
2025-09-22 08:05:36 +00:00
ruthra kumar
8c7e20f9a2 Merge pull request #49664 from frappe/mergify/bp/version-15-hotfix/pr-49470
fix(bank clearance): use base total taxes and charges if exists (backport #49470)
2025-09-22 12:44:55 +05:30
Ravibharathi
81824320db Merge pull request #49470 from aerele/bank-clearance-tax-calculation
fix(bank clearance): use base total taxes and charges if exists

(cherry picked from commit 302ff49b7f)
2025-09-22 05:58:20 +00:00
Diptanil Saha
9914d20127 Merge pull request #49659 from frappe/mergify/bp/version-15-hotfix/pr-49649
fix: set first_response_time on set_first_response (backport #49649)
2025-09-22 00:38:16 +05:30
diptanilsaha
0d5613ca4c fix: set first_response_time on status change of issue
(cherry picked from commit ba459204b0)
2025-09-21 18:36:29 +00:00
Frappe PR Bot
20d14881fe chore(release): Bumped to Version 15.79.1
## [15.79.1](https://github.com/frappe/erpnext/compare/v15.79.0...v15.79.1) (2025-09-21)

### Bug Fixes

* **stock:** NoneType object error on stock entry ([48bd005](48bd005a09))
2025-09-21 18:34:45 +00:00
rohitwaghchaure
73e76f533e Merge pull request #49658 from frappe/mergify/bp/version-15/pr-49619
fix(stock): NoneType object error on stock entry (backport #49615) (backport #49619)
2025-09-22 00:03:18 +05:30
Kavin
48bd005a09 fix(stock): NoneType object error on stock entry
(cherry picked from commit aee03417de)
(cherry picked from commit de8c4eec1b)
2025-09-21 17:12:19 +00:00
Mihir Kandoi
04c2bb4f50 Merge pull request #49619 from frappe/mergify/bp/version-15-hotfix/pr-49615
fix(stock): NoneType object error on stock entry (backport #49615)
2025-09-18 21:18:09 +05:30
Kavin
de8c4eec1b fix(stock): NoneType object error on stock entry
(cherry picked from commit aee03417de)
2025-09-18 14:47:04 +00:00
mergify[bot]
697f6ef086 fix: skip receivable/payable account validation in payroll entry if party is not available (backport #49585) (#49598)
fix: skip receivable/payable account validation in payroll entry if party is not available (#49585)

* fix: skip receivable/payable account validation if party is not available in creation of payroll entry

* refactor: rename flag

(cherry picked from commit 8b543e5503)

Co-authored-by: Raheel Khan <raheel@frappe.io>
2025-09-18 17:04:28 +05:30
ruthra kumar
d66aed52f7 Merge pull request #49617 from frappe/mergify/bp/version-15-hotfix/pr-49467
fix: add condition for name (backport #49467)
2025-09-18 17:03:32 +05:30
venkat102
089c068ee8 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:46:39 +05:30
venkat102
65ce737c2d fix: add condition for name
(cherry picked from commit cf5a2d6351)
2025-09-18 11:12:13 +00:00
mergify[bot]
5ef7a7857c fix(accounts): dynamic zero cutoff (backport #48899) (#49552)
fix(accounts): dynamic zero cutoff (#48899)

* fix(accounts): dynamic zero cutoff

The cutoff for displaying zero values in accounting reports has so far been hardcoded to 0.005, giving wrong results for currencies that require a higher precision. This PR changes this to a dynamic value calculated from the Currency's _Fraction Units_.

* style: fix typo

(cherry picked from commit 6972f161b8)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2025-09-17 18:03:48 +05:30
mergify[bot]
fbd62e72f9 fix(payment-reconciliation): apply field precision for allocated and difference amounts (backport #49448) (#49595)
fix(payment-reconciliation): apply field precision for allocated and difference amounts (#49448)

fix(payment-reconciliation): handle allocated and difference amount with field precision for accurate exchange rate calculations

(cherry picked from commit 194ab87fef)

Co-authored-by: Bhavansathru <122002510+Bhavan23@users.noreply.github.com>
2025-09-17 17:33:33 +05:30
mergify[bot]
1a4ea07b78 fix: add option for currency field (backport #49572) (#49594)
fix: add option for currency field (#49572)

(cherry picked from commit e0299e1cbd)

Co-authored-by: Logesh Periyasamy <logeshperiyasamy24@gmail.com>
2025-09-17 17:33:15 +05:30
mergify[bot]
bce22cb27b fix: project accounting dimension and gl posting date column width (backport #49563) (#49591)
fix: project accounting dimension and gl posting date column width (#49563)

* fix: add width for posting_date

* fix: add project field for missing doctype

* fix: remove allow_on_submit to align with cost center

(cherry picked from commit 9fc17e0e3a)

Co-authored-by: Logesh Periyasamy <logeshperiyasamy24@gmail.com>
2025-09-17 17:33:02 +05:30
Imesha Sudasingha
9e58a56b5c fix(item): avoid inheriting item defaults from identically named items (#49571) 2025-09-17 17:32:13 +05:30
rohitwaghchaure
87a2774511 Merge pull request #49592 from frappe/mergify/bp/version-15-hotfix/pr-49587
fix: incorrect current qty calculation for the batch (backport #49587)
2025-09-17 17:21:26 +05:30
Rohit Waghchaure
bf48cf02e5 fix: incorrect current qty calculation for the batch
(cherry picked from commit 535f8657ed)
2025-09-17 11:32:11 +00:00
46 changed files with 361 additions and 63 deletions

View File

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

View File

@@ -16,6 +16,7 @@
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"section_break_8",
"rate",
"section_break_9",
@@ -92,6 +93,13 @@
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"fieldname": "section_break_8",
"fieldtype": "Section Break"

View File

@@ -137,8 +137,10 @@ def get_payment_entries_for_bank_clearance(
entries = []
condition = ""
pe_condition = ""
if not include_reconciled_entries:
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
pe_condition = "and (pe.clearance_date IS NULL or pe.clearance_date='0000-00-00')"
journal_entries = frappe.db.sql(
f"""
@@ -163,19 +165,20 @@ def get_payment_entries_for_bank_clearance(
payment_entries = frappe.db.sql(
f"""
select
"Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date,
if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit,
if(paid_from=%(account)s, 0, received_amount + total_taxes_and_charges) as debit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
from `tabPayment Entry`
"Payment Entry" as payment_document, pe.name as payment_entry,
pe.reference_no as cheque_number, pe.reference_date as cheque_date,
if(pe.paid_from=%(account)s, pe.paid_amount + if(pe.payment_type = 'Pay' and c.default_currency = pe.paid_from_account_currency, pe.base_total_taxes_and_charges, pe.total_taxes_and_charges) , 0) as credit,
if(pe.paid_from=%(account)s, 0, pe.received_amount + pe.total_taxes_and_charges) as debit,
pe.posting_date, ifnull(pe.party,if(pe.paid_from=%(account)s,pe.paid_to,pe.paid_from)) as against_account, pe.clearance_date,
if(pe.paid_to=%(account)s, pe.paid_to_account_currency, pe.paid_from_account_currency) as account_currency
from `tabPayment Entry` as pe
join `tabCompany` c on c.name = pe.company
where
(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
and posting_date >= %(from)s and posting_date <= %(to)s
{condition}
(pe.paid_from=%(account)s or pe.paid_to=%(account)s) and pe.docstatus=1
and pe.posting_date >= %(from)s and pe.posting_date <= %(to)s
{pe_condition}
order by
posting_date ASC, name DESC
pe.posting_date ASC, pe.name DESC
""",
{
"account": account,

View File

@@ -131,18 +131,20 @@ class GLEntry(Document):
if not self.is_cancelled and not (self.party_type and self.party):
account_type = frappe.get_cached_value("Account", self.account, "account_type")
if account_type == "Receivable":
frappe.throw(
_("{0} {1}: Customer is required against Receivable account {2}").format(
self.voucher_type, self.voucher_no, self.account
# skipping validation for payroll entry creation in case party is not required
if not frappe.flags.party_not_required_for_receivable_payable:
if account_type == "Receivable":
frappe.throw(
_("{0} {1}: Customer is required against Receivable account {2}").format(
self.voucher_type, self.voucher_no, self.account
)
)
)
elif account_type == "Payable":
frappe.throw(
_("{0} {1}: Supplier is required against Payable account {2}").format(
self.voucher_type, self.voucher_no, self.account
elif account_type == "Payable":
frappe.throw(
_("{0} {1}: Supplier is required against Payable account {2}").format(
self.voucher_type, self.voucher_no, self.account
)
)
)
# Zero value transaction is not allowed
if not (

View File

@@ -542,8 +542,11 @@ class JournalEntry(AccountsController):
def validate_party(self):
for d in self.get("accounts"):
account_type = frappe.get_cached_value("Account", d.account, "account_type")
# skipping validation for payroll entry creation
skip_validation = frappe.flags.party_not_required_for_receivable_payable
if account_type in ["Receivable", "Payable"]:
if not (d.party_type and d.party):
if not (d.party_type and d.party) and not skip_validation:
frappe.throw(
_(
"Row {0}: Party Type and Party is required for Receivable / Payable account {1}"

View File

@@ -24,6 +24,7 @@
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"help_section",
"loyalty_program_help"
],
@@ -143,6 +144,12 @@
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
}
],
"modified": "2019-05-26 09:11:46.120251",

View File

@@ -13,6 +13,7 @@
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"section_break_4",
"invoices"
],
@@ -62,6 +63,12 @@
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",

View File

@@ -28,6 +28,7 @@
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"sec_break1",
"invoice_name",
"invoices",
@@ -193,6 +194,12 @@
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"depends_on": "eval:doc.party",
"description": "Only 'Payment Entries' made against this advance account are supported.",

View File

@@ -5,6 +5,7 @@
import frappe
from frappe import _, msgprint, qb
from frappe.model.document import Document
from frappe.model.meta import get_field_precision
from frappe.query_builder import Criterion
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, fmt_money, get_link_to_form, getdate, nowdate, today
@@ -392,6 +393,12 @@ class PaymentReconciliation(Document):
inv.outstanding_amount = flt(entry.get("outstanding_amount"))
def get_difference_amount(self, payment_entry, invoice, allocated_amount):
allocated_amount_precision = get_field_precision(
frappe.get_meta("Payment Reconciliation Allocation").get_field("allocated_amount")
)
difference_amount_precision = get_field_precision(
frappe.get_meta("Payment Reconciliation Allocation").get_field("difference_amount")
)
difference_amount = 0
if frappe.get_cached_value(
"Account", self.receivable_payable_account, "account_currency"
@@ -399,8 +406,14 @@ class PaymentReconciliation(Document):
if invoice.get("exchange_rate") and payment_entry.get("exchange_rate", 1) != invoice.get(
"exchange_rate", 1
):
allocated_amount_in_ref_rate = payment_entry.get("exchange_rate", 1) * allocated_amount
allocated_amount_in_inv_rate = invoice.get("exchange_rate", 1) * allocated_amount
allocated_amount_in_ref_rate = flt(
payment_entry.get("exchange_rate", 1) * flt(allocated_amount, allocated_amount_precision),
difference_amount_precision,
)
allocated_amount_in_inv_rate = flt(
invoice.get("exchange_rate", 1) * flt(allocated_amount, allocated_amount_precision),
difference_amount_precision,
)
difference_amount = allocated_amount_in_ref_rate - allocated_amount_in_inv_rate
return difference_amount

View File

@@ -22,6 +22,7 @@
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"section_break_9",
"account_currency",
"tax_amount",
@@ -211,6 +212,13 @@
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"default": "0",
"depends_on": "eval:['Purchase Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",

View File

@@ -16,6 +16,7 @@
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"section_break_8",
"rate",
"section_break_9",
@@ -188,6 +189,13 @@
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"default": "0",
"depends_on": "eval:['Sales Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",

View File

@@ -16,6 +16,7 @@
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"shipping_amount_section",
"calculate_based_on",
"column_break_8",
@@ -136,6 +137,12 @@
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
}
],
"icon": "fa fa-truck",

View File

@@ -164,6 +164,12 @@
{% } %}
</tr>
</thead>
<div class="show-filters">
{% if subtitle %}
{{ subtitle }}
<hr>
{% endif %}
</div>
<tbody>
{% for(var i=0, l=data.length; i<l; i++) { %}
<tr>

View File

@@ -974,6 +974,7 @@ class ReceivablePayableReport:
if self.account_type == "Receivable":
self.add_customer_filters()
self.exclude_employee_transaction()
elif self.account_type == "Payable":
self.add_supplier_filters()
@@ -1053,6 +1054,9 @@ class ReceivablePayableReport:
)
)
def exclude_employee_transaction(self):
self.qb_selection_filter.append(self.ple.party_type != "Employee")
def add_supplier_filters(self):
supplier = qb.DocType("Supplier")
if self.filters.get("supplier_group"):

View File

@@ -38,6 +38,7 @@ from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement
get_report_summary as get_pl_summary,
)
from erpnext.accounts.report.utils import convert, convert_to_presentation_currency
from erpnext.accounts.utils import get_zero_cutoff
def execute(filters=None):
@@ -563,7 +564,7 @@ def prepare_data(accounts, start_date, end_date, balance_must_be, companies, com
row[company] = flt(d.get(company, 0.0), 3)
if abs(row[company]) >= 0.005:
if abs(row[company]) >= get_zero_cutoff(filters.presentation_currency):
# ignore zero values
has_value = True
total += flt(row[company])

View File

@@ -12,6 +12,7 @@ from erpnext.accounts.report.financial_statements import (
filter_out_zero_value_rows,
)
from erpnext.accounts.report.trial_balance.trial_balance import validate_filters
from erpnext.accounts.utils import get_zero_cutoff
def execute(filters=None):
@@ -154,7 +155,7 @@ def prepare_data(accounts, filters, company_currency, dimension_list):
for dimension in dimension_list:
row[frappe.scrub(dimension)] = flt(d.get(frappe.scrub(dimension), 0.0), 3)
if abs(row[frappe.scrub(dimension)]) >= 0.005:
if abs(row[frappe.scrub(dimension)]) >= get_zero_cutoff(company_currency):
# ignore zero values
has_value = True
total += flt(d.get(frappe.scrub(dimension), 0.0), 3)

View File

@@ -34,6 +34,12 @@
</h5>
{% } %}
<hr>
<div class="show-filters">
{% if subtitle %}
{{ subtitle }}
<hr>
{% endif %}
</div>
<table class="table table-bordered">
<thead>
<tr>

View File

@@ -18,7 +18,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_dimension_with_children,
)
from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency
from erpnext.accounts.utils import get_fiscal_year
from erpnext.accounts.utils import get_fiscal_year, get_zero_cutoff
def get_period_list(
@@ -304,7 +304,7 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency, accum
row[period.key] = flt(d.get(period.key, 0.0), 3)
if abs(row[period.key]) >= 0.005:
if abs(row[period.key]) >= get_zero_cutoff(company_currency):
# ignore zero values
has_value = True
total += flt(row[period.key])

View File

@@ -21,6 +21,12 @@
{%= frappe.datetime.str_to_user(filters.to_date) %}
</h5>
<hr>
<div class="show-filters">
{% if subtitle %}
{{ subtitle }}
<hr>
{% endif %}
</div>
<table class="table table-bordered">
<thead>
<tr>

View File

@@ -645,7 +645,7 @@ def get_columns(filters):
"options": "GL Entry",
"hidden": 1,
},
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 120},
{
"label": _("Account"),
"fieldname": "account",

View File

@@ -12,6 +12,7 @@ from erpnext.accounts.report.financial_statements import (
filter_out_zero_value_rows,
)
from erpnext.accounts.report.trial_balance.trial_balance import validate_filters
from erpnext.accounts.utils import get_zero_cutoff
value_fields = ("income", "expense", "gross_profit_loss")
@@ -149,7 +150,7 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
for key in value_fields:
row[key] = flt(d.get(key, 0.0), 3)
if abs(row[key]) >= 0.005:
if abs(row[key]) >= get_zero_cutoff(company_currency):
# ignore zero values
has_value = True

View File

@@ -18,6 +18,7 @@ from erpnext.accounts.report.financial_statements import (
set_gl_entries_by_account,
)
from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency
from erpnext.accounts.utils import get_zero_cutoff
value_fields = (
"opening_debit",
@@ -413,7 +414,7 @@ def prepare_data(accounts, filters, parent_children_map, company_currency):
for key in value_fields:
row[key] = flt(d.get(key, 0.0), 3)
if abs(row[key]) >= 0.005:
if abs(row[key]) >= get_zero_cutoff(company_currency):
# ignore zero values
has_value = True

View File

@@ -9,6 +9,7 @@ from erpnext.accounts.party import get_party_shipping_address
from erpnext.accounts.utils import (
get_future_stock_vouchers,
get_voucherwise_gl_entries,
get_zero_cutoff,
sort_stock_vouchers_by_posting_date,
)
from erpnext.stock.doctype.item.test_item import make_item
@@ -156,6 +157,11 @@ class TestUtils(unittest.TestCase):
self.assertSequenceEqual(doc_name[0:2], ("SUP", fiscal_year))
frappe.db.set_default("supp_master_name", "Supplier Name")
def test_get_zero_cutoff(self):
self.assertEqual(get_zero_cutoff(None), 0.005)
self.assertEqual(get_zero_cutoff("EUR"), 0.005)
self.assertEqual(get_zero_cutoff("BHD"), 0.0005)
ADDRESS_RECORDS = [
{

View File

@@ -27,6 +27,7 @@ from frappe.utils import (
now,
nowdate,
)
from frappe.utils.caching import site_cache
from pypika import Order
from pypika.functions import Coalesce
from pypika.terms import ExistsCriterion
@@ -1130,6 +1131,29 @@ def get_currency_precision():
return precision
def get_fraction_units(currency: str) -> int:
"""Returns the number of fraction units for a currency."""
fraction_units = frappe.db.get_value("Currency", currency, "fraction_units")
if fraction_units is None:
fraction_units = 100
return fraction_units
@site_cache()
def get_zero_cutoff(currency: str) -> float:
"""Returns the zero cutoff for a currency.
For example, if the Fraction Units for a currency are set to 100, then the zero cutoff is 0.005.
We don't want to display values less than the zero cutoff.
This value was chosen for compatibility with the previous hard-coded value of 0.005.
"""
fraction_units = get_fraction_units(currency)
return 0.5 / (fraction_units or 1)
def get_held_invoices(party_type, party):
"""
Returns a list of names Purchase Invoices for the given party that are on hold
@@ -2451,6 +2475,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]
@@ -2459,6 +2487,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

@@ -44,6 +44,7 @@
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"target_fixed_asset_account"
],
"fields": [
@@ -288,6 +289,12 @@
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"

View File

@@ -18,6 +18,7 @@
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"fixed_asset_account"
],
"fields": [
@@ -98,6 +99,13 @@
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"fieldname": "finance_book",
"fieldtype": "Link",

View File

@@ -118,7 +118,8 @@
"fieldname": "rate",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Rate"
"label": "Rate",
"options": "currency"
},
{
"columns": 1,
@@ -161,7 +162,8 @@
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount",
"read_only": 1
"read_only": 1,
"options": "currency"
},
{
"fieldname": "column_break_yuca",
@@ -183,13 +185,15 @@
"fieldname": "base_amount",
"fieldtype": "Currency",
"hidden": 1,
"label": "Base Amount"
"label": "Base Amount",
"options": "Company:company:default_currency"
},
{
"fieldname": "base_rate",
"fieldtype": "Currency",
"hidden": 1,
"label": "Base Rate"
"label": "Base Rate",
"options": "Company:company:default_currency"
},
{
"default": "0",

View File

@@ -6,7 +6,10 @@ def execute():
"POS Invoice Merge Log", {"docstatus": 1}, ["name", "pos_closing_entry"]
)
frappe.db.auto_commit_on_many_writes = 1
for log in pos_invoice_merge_logs:
if log.pos_closing_entry and frappe.db.exists("POS Closing Entry", log.pos_closing_entry):
company = frappe.db.get_value("POS Closing Entry", log.pos_closing_entry, "company")
frappe.db.set_value("POS Invoice Merge Log", log.name, "company", company)
frappe.db.auto_commit_on_many_writes = 0

View File

@@ -931,7 +931,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
party_name = me.frm.doc.party_name
}
else{
party_type = frappe.meta.has_field(me.frm.doc.doctype, "customer") ? "Customer" : "Supplier";
party_type = frappe.meta.has_field(me.frm.doc.doctype, "supplier") ? "Supplier" : "Customer";
party_name = me.frm.doc[party_type.toLowerCase()];
}
if (party_name) {

View File

@@ -1762,6 +1762,11 @@ def create_pick_list(source_name, target_doc=None):
target.qty = qty_to_be_picked
target.stock_qty = qty_to_be_picked * flt(source.conversion_factor)
# update available qty
bin_details = get_bin_details(source.item_code, source.warehouse, source_parent.company)
target.actual_qty = bin_details.get("actual_qty")
target.company_total_stock = bin_details.get("company_total_stock")
def update_packed_item_qty(source, target, source_parent) -> None:
qty = flt(source.qty)
for item in source_parent.items:

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

@@ -218,6 +218,7 @@ def get_batch_qty(
batch_no=None,
warehouse=None,
item_code=None,
creation=None,
posting_date=None,
posting_time=None,
ignore_voucher_nos=None,
@@ -244,6 +245,7 @@ def get_batch_qty(
{
"item_code": item_code,
"warehouse": warehouse,
"creation": creation,
"posting_date": posting_date,
"posting_time": posting_time,
"batch_no": batch_no,

View File

@@ -724,7 +724,10 @@ class Item(Document):
item_defaults = frappe.db.get_values(
"Item Default",
{"parent": self.item_group},
{
"parent": self.item_group,
"parenttype": "Item Group",
},
[
"company",
"default_warehouse",

View File

@@ -354,10 +354,12 @@ frappe.ui.form.on("Pick List Item", {
item_code: (frm, cdt, cdn) => {
let row = frappe.get_doc(cdt, cdn);
if (row.item_code) {
get_item_details(row.item_code).then((data) => {
get_item_details(row.item_code, row.uom, row.warehouse, frm.doc.company).then((data) => {
frappe.model.set_value(cdt, cdn, "uom", data.stock_uom);
frappe.model.set_value(cdt, cdn, "stock_uom", data.stock_uom);
frappe.model.set_value(cdt, cdn, "conversion_factor", 1);
frappe.model.set_value(cdt, cdn, "actual_qty", data.actual_qty);
frappe.model.set_value(cdt, cdn, "company_total_stock", data.company_total_stock);
});
}
},
@@ -371,6 +373,15 @@ frappe.ui.form.on("Pick List Item", {
}
},
warehouse: (frm, cdt, cdn) => {
const row = frappe.get_doc(cdt, cdn);
if (!row.item_code || !row.warehouse) return;
get_item_details(row.item_code, row.uom, row.warehouse, frm.doc.company).then((data) => {
frappe.model.set_value(cdt, cdn, "actual_qty", data.actual_qty);
frappe.model.set_value(cdt, cdn, "company_total_stock", data.company_total_stock);
});
},
qty: (frm, cdt, cdn) => {
let row = frappe.get_doc(cdt, cdn);
frappe.model.set_value(cdt, cdn, "stock_qty", row.qty * row.conversion_factor);
@@ -412,11 +423,13 @@ frappe.ui.form.on("Pick List Item", {
},
});
function get_item_details(item_code, uom = null) {
function get_item_details(item_code, uom = null, warehouse = null, company = null) {
if (item_code) {
return frappe.xcall("erpnext.stock.doctype.pick_list.pick_list.get_item_details", {
item_code,
uom,
warehouse,
company,
});
}
}

View File

@@ -21,7 +21,7 @@ from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle impor
get_auto_batch_nos,
get_picked_serial_nos,
)
from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.stock.get_item_details import get_company_total_stock, get_conversion_factor
from erpnext.stock.serial_batch_bundle import (
SerialBatchCreation,
get_batches_from_bundle,
@@ -74,6 +74,9 @@ class PickList(TransactionBase):
if self.has_reserved_stock():
self.set_onload("has_reserved_stock", True)
for item in self.get("locations"):
item.update(get_item_details(item.item_code, item.uom, item.warehouse, self.company))
def validate(self):
self.validate_expired_batches()
self.validate_for_qty()
@@ -1442,15 +1445,29 @@ def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filte
@frappe.whitelist()
def get_item_details(item_code, uom=None):
def get_item_details(item_code, uom=None, warehouse=None, company=None):
details = frappe.db.get_value("Item", item_code, ["stock_uom", "name"], as_dict=1)
details.uom = uom or details.stock_uom
if uom:
details.update(get_conversion_factor(item_code, uom))
if warehouse:
details.actual_qty = flt(get_actual_qty(item_code, warehouse))
if company:
details.company_total_stock = get_company_total_stock(item_code, company)
return details
def get_actual_qty(item_code, warehouse):
return frappe.db.get_value(
"Bin",
{"item_code": item_code, "warehouse": warehouse},
"actual_qty",
)
def update_delivery_note_item(source, target, delivery_note):
cost_center = frappe.db.get_value("Project", delivery_note.project, "cost_center")
if not cost_center:

View File

@@ -22,6 +22,10 @@
"conversion_factor",
"stock_uom",
"delivered_qty",
"available_quantity_section",
"actual_qty",
"column_break_kyek",
"company_total_stock",
"serial_no_and_batch_section",
"pick_serial_and_batch",
"serial_and_batch_bundle",
@@ -124,7 +128,7 @@
"fieldname": "stock_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Stock Qty",
"label": "Qty (in Stock UOM)",
"read_only": 1
},
{
@@ -248,11 +252,38 @@
"print_hide": 1,
"read_only": 1,
"report_hide": 1
},
{
"fieldname": "available_quantity_section",
"fieldtype": "Section Break",
"label": "Available Qty"
},
{
"allow_on_submit": 1,
"fieldname": "actual_qty",
"fieldtype": "Float",
"label": "Qty (Warehouse)",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "column_break_kyek",
"fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "company_total_stock",
"fieldtype": "Float",
"label": "Qty (Company)",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
}
],
"istable": 1,
"links": [],
"modified": "2025-05-31 19:57:43.531298",
"modified": "2025-09-23 00:02:57.817040",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List Item",

View File

@@ -15,7 +15,9 @@ class PickListItem(Document):
if TYPE_CHECKING:
from frappe.types import DF
actual_qty: DF.Float
batch_no: DF.Link | None
company_total_stock: DF.Float
conversion_factor: DF.Float
delivered_qty: DF.Float
description: DF.Text | None

View File

@@ -774,7 +774,9 @@ class SerialandBatchBundle(Document):
(parent.warehouse == self.warehouse) & (parent.voucher_type == "Stock Reconciliation")
)
elif batches:
future_entries = future_entries.where(child.batch_no.isin(batches))
future_entries = future_entries.where(
(child.batch_no.isin(batches)) & (parent.warehouse == self.warehouse)
)
future_entries = future_entries.run(as_dict=True)
@@ -2358,6 +2360,16 @@ def get_available_batches(kwargs):
kwargs.posting_date, kwargs.posting_time
)
if kwargs.get("creation"):
timestamp_condition = stock_ledger_entry.posting_datetime < get_combine_datetime(
kwargs.posting_date, kwargs.posting_time
)
timestamp_condition |= (
stock_ledger_entry.posting_datetime
== get_combine_datetime(kwargs.posting_date, kwargs.posting_time)
) & (stock_ledger_entry.creation < kwargs.creation)
query = query.where(timestamp_condition)
for field in ["warehouse", "item_code"]:
@@ -2599,6 +2611,16 @@ def get_stock_ledgers_for_serial_nos(kwargs):
kwargs.posting_date, kwargs.posting_time
)
if kwargs.get("creation"):
timestamp_condition = stock_ledger_entry.posting_datetime < get_combine_datetime(
kwargs.posting_date, kwargs.posting_time
)
timestamp_condition |= (
stock_ledger_entry.posting_datetime
== get_combine_datetime(kwargs.posting_date, kwargs.posting_time)
) & (stock_ledger_entry.creation < kwargs.creation)
query = query.where(timestamp_condition)
for field in ["warehouse", "item_code", "serial_no"]:
@@ -2657,6 +2679,16 @@ def get_stock_ledgers_batches(kwargs):
kwargs.posting_date, kwargs.posting_time
)
if kwargs.get("creation"):
timestamp_condition = stock_ledger_entry.posting_datetime < get_combine_datetime(
kwargs.posting_date, kwargs.posting_time
)
timestamp_condition |= (
stock_ledger_entry.posting_datetime
== get_combine_datetime(kwargs.posting_date, kwargs.posting_time)
) & (stock_ledger_entry.creation < kwargs.creation)
query = query.where(timestamp_condition)
if kwargs.get("ignore_voucher_nos"):

View File

@@ -2123,9 +2123,12 @@ class StockEntry(StockController):
"Work Order", self.work_order, "allow_alternative_item"
)
skip_transfer, from_wip_warehouse = frappe.get_value(
"Work Order", self.work_order, ["skip_transfer", "from_wip_warehouse"]
skip_transfer, from_wip_warehouse = (
frappe.get_value("Work Order", self.work_order, ["skip_transfer", "from_wip_warehouse"])
if self.work_order
else [None, None]
)
item.from_warehouse = (
frappe.get_value(
"Work Order Item",

View File

@@ -5,7 +5,7 @@
import frappe
from frappe import _, bold, json, msgprint
from frappe.query_builder.functions import CombineDatetime, Sum
from frappe.utils import add_to_date, cint, cstr, flt, get_datetime
from frappe.utils import add_to_date, cint, cstr, flt, get_datetime, now
import erpnext
from erpnext.accounts.utils import get_company_default
@@ -1034,7 +1034,7 @@ class StockReconciliation(StockController):
val_rate = 0.0
current_qty = 0.0
if row.current_serial_and_batch_bundle:
current_qty = self.get_current_qty_for_serial_or_batch(row)
current_qty = self.get_current_qty_for_serial_or_batch(row, sle_creation)
elif row.serial_no:
item_dict = get_stock_balance_for(
row.item_code,
@@ -1143,17 +1143,17 @@ class StockReconciliation(StockController):
return allow_negative_stock
def get_current_qty_for_serial_or_batch(self, row):
def get_current_qty_for_serial_or_batch(self, row, sle_creation):
doc = frappe.get_doc("Serial and Batch Bundle", row.current_serial_and_batch_bundle)
current_qty = 0.0
if doc.has_serial_no:
current_qty = self.get_current_qty_for_serial_nos(doc)
current_qty = self.get_current_qty_for_serial_nos(doc, sle_creation)
elif doc.has_batch_no:
current_qty = self.get_current_qty_for_batch_nos(doc)
current_qty = self.get_current_qty_for_batch_nos(doc, sle_creation)
return abs(current_qty)
def get_current_qty_for_serial_nos(self, doc):
def get_current_qty_for_serial_nos(self, doc, sle_creation):
serial_nos_details = get_available_serial_nos(
frappe._dict(
{
@@ -1161,6 +1161,7 @@ class StockReconciliation(StockController):
"warehouse": doc.warehouse,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"creation": sle_creation,
"voucher_no": self.name,
"ignore_warehouse": 1,
}
@@ -1190,7 +1191,7 @@ class StockReconciliation(StockController):
return current_qty
def get_current_qty_for_batch_nos(self, doc):
def get_current_qty_for_batch_nos(self, doc, sle_creation):
current_qty = 0.0
precision = doc.entries[0].precision("qty")
for d in doc.entries:
@@ -1198,6 +1199,7 @@ class StockReconciliation(StockController):
get_batch_qty(
d.batch_no,
doc.warehouse,
creation=sle_creation,
posting_date=doc.posting_date,
posting_time=doc.posting_time,
ignore_voucher_nos=[doc.voucher_no],
@@ -1494,6 +1496,7 @@ def get_stock_balance_for(
"company": company,
"posting_date": posting_date,
"posting_time": posting_time,
"creation": row.get("creation") if row and row.get("creation") else now(),
}
)
)

View File

@@ -26,7 +26,7 @@ import erpnext
from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
get_available_batches,
get_auto_batch_nos,
)
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
get_sre_reserved_batch_nos_details,
@@ -2213,7 +2213,7 @@ def validate_reserved_serial_nos(item_code, warehouse, serial_nos):
def validate_reserved_batch_nos(item_code, warehouse, batch_nos):
if reserved_batches_map := get_sre_reserved_batch_nos_details(item_code, warehouse, batch_nos):
available_batches = get_available_batches(
available_batches = get_auto_batch_nos(
frappe._dict(
{
"item_code": item_code,

View File

@@ -312,7 +312,7 @@ def is_first_response(issue):
def calculate_first_response_time(issue, first_responded_on):
issue_creation_date = issue.service_level_agreement_creation or issue.creation
issue_creation_date = get_datetime(issue.service_level_agreement_creation or issue.creation)
issue_creation_time = get_time_in_seconds(issue_creation_date)
first_responded_on_in_seconds = get_time_in_seconds(first_responded_on)
support_hours = frappe.get_cached_doc(

View File

@@ -25,7 +25,7 @@ from frappe.utils.caching import redis_cache
from frappe.utils.nestedset import get_ancestors_of
from frappe.utils.safe_exec import get_safe_globals
from erpnext.support.doctype.issue.issue import get_holidays
from erpnext.support.doctype.issue.issue import calculate_first_response_time, get_holidays
class ServiceLevelAgreement(Document):
@@ -552,6 +552,8 @@ def handle_status_change(doc, apply_sla_for_resolution):
def set_first_response():
if doc.meta.has_field("first_responded_on") and not doc.get("first_responded_on"):
doc.first_responded_on = now_time
if doc.meta.has_field("first_response_time"):
doc.first_response_time = calculate_first_response_time(doc, doc.first_responded_on)
if get_datetime(doc.get("first_responded_on")) > get_datetime(doc.get("response_by")):
record_assigned_users_on_failure(doc)

View File

@@ -5,5 +5,6 @@ from erpnext.utilities.activation import get_level
class TestActivation(FrappeTestCase):
def test_activation(self):
levels = get_level()
site_info = {"activation": {"activation_level": 0, "sales_data": []}}
levels = get_level(site_info)
self.assertTrue(levels)

View File

@@ -37,7 +37,7 @@ def get_site_info(site_info):
if company:
domain = frappe.get_cached_value("Company", cstr(company), "domain")
return {"company": company, "domain": domain, "activation": get_level()}
return {"company": company, "domain": domain, "activation": get_level(site_info)}
@contextmanager

View File

@@ -9,9 +9,9 @@ from frappe.core.doctype.installed_applications.installed_applications import ge
import erpnext
def get_level():
activation_level = 0
sales_data = []
def get_level(site_info):
activation_level = site_info.get("activation", {}).get("activation_level", 0)
sales_data = site_info.get("activation", {}).get("sales_data", [])
min_count = 0
doctypes = {
"Asset": 5,