diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
index 46ba27c004d..28e79b5d2c6 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
@@ -12,6 +12,9 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
},
};
});
+ let no_bank_transactions_text =
+ `
${__("No Matching Bank Transactions Found")}
`
+ set_field_options("no_bank_transactions", no_bank_transactions_text);
},
onload: function (frm) {
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
index b643e6e0912..f666101d3fd 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
@@ -81,8 +81,7 @@
},
{
"fieldname": "no_bank_transactions",
- "fieldtype": "HTML",
- "options": "No Matching Bank Transactions Found
"
+ "fieldtype": "HTML"
}
],
"hide_toolbar": 1,
@@ -109,4 +108,4 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
index f74562086ef..04af32346bb 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js
@@ -100,7 +100,7 @@ frappe.ui.form.on("Bank Statement Import", {
if (frm.doc.status.includes("Success")) {
frm.add_custom_button(
- __("Go to {0} List", [frm.doc.reference_doctype]),
+ __("Go to {0} List", [__(frm.doc.reference_doctype)]),
() => frappe.set_route("List", frm.doc.reference_doctype)
);
}
diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
index 3f85668eded..4bb18655b47 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
@@ -8,6 +8,7 @@
"engine": "InnoDB",
"field_order": [
"barcode",
+ "has_item_scanned",
"item_code",
"col_break1",
"item_name",
@@ -808,11 +809,19 @@
"fieldtype": "Check",
"label": "Grant Commission",
"read_only": 1
+ },
+ {
+ "default": "0",
+ "depends_on": "barcode",
+ "fieldname": "has_item_scanned",
+ "fieldtype": "Check",
+ "label": "Has Item Scanned",
+ "read_only": 1
}
],
"istable": 1,
"links": [],
- "modified": "2021-10-05 12:23:47.506290",
+ "modified": "2022-11-02 12:52:39.125295",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Item",
@@ -820,5 +829,6 @@
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index e73d6023328..370c0fc9605 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -57,6 +57,8 @@
"column_break_28",
"total",
"net_total",
+ "tax_withholding_net_total",
+ "base_tax_withholding_net_total",
"taxes_section",
"taxes_and_charges",
"column_break_58",
@@ -89,7 +91,6 @@
"section_break_44",
"apply_discount_on",
"base_discount_amount",
- "additional_discount_account",
"column_break_46",
"additional_discount_percentage",
"discount_amount",
@@ -1421,6 +1422,26 @@
"label": "Is Old Subcontracting Flow",
"read_only": 1
},
+ {
+ "default": "0",
+ "fieldname": "tax_withholding_net_total",
+ "fieldtype": "Currency",
+ "hidden": 1,
+ "label": "Tax Withholding Net Total",
+ "no_copy": 1,
+ "options": "currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "base_tax_withholding_net_total",
+ "fieldtype": "Currency",
+ "hidden": 1,
+ "label": "Base Tax Withholding Net Total",
+ "no_copy": 1,
+ "options": "currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
"collapsible_depends_on": "tax_withheld_vouchers",
"fieldname": "tax_withheld_vouchers_section",
@@ -1519,7 +1540,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
- "modified": "2022-10-11 13:04:44.304389",
+ "modified": "2022-11-04 01:02:44.544878",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index a8f6f80b6b9..5c1cb0dcc68 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -40,6 +40,7 @@
"discount_amount",
"base_rate_with_margin",
"sec_break2",
+ "apply_tds",
"rate",
"amount",
"item_tax_template",
@@ -868,6 +869,12 @@
"label": "Product Bundle",
"options": "Product Bundle",
"read_only": 1
+ },
+ {
+ "default": "1",
+ "fieldname": "apply_tds",
+ "fieldtype": "Check",
+ "label": "Apply TDS"
}
],
"idx": 1,
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 7f1a1eccc45..77055f94455 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -8,6 +8,7 @@
"engine": "InnoDB",
"field_order": [
"barcode",
+ "has_item_scanned",
"item_code",
"col_break1",
"item_name",
@@ -872,12 +873,20 @@
"label": "Purchase Order Item",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "default": "0",
+ "depends_on": "barcode",
+ "fieldname": "has_item_scanned",
+ "fieldtype": "Check",
+ "label": "Has Item Scanned",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2022-10-26 11:38:36.119339",
+ "modified": "2022-11-02 12:53:12.693217",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 7eddd81ee03..30ed91b9744 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -61,6 +61,9 @@ def get_party_details(inv):
def get_party_tax_withholding_details(inv, tax_withholding_category=None):
+ if inv.doctype == "Payment Entry":
+ inv.tax_withholding_net_total = inv.net_total
+
pan_no = ""
parties = []
party_type, party = get_party_details(inv)
@@ -242,7 +245,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
if party_type == "Supplier":
ldc = get_lower_deduction_certificate(tax_details, pan_no)
if tax_deducted:
- net_total = inv.net_total
+ net_total = inv.tax_withholding_net_total
if ldc:
tax_amount = get_tds_amount_from_ldc(
ldc, parties, pan_no, tax_details, posting_date, net_total
@@ -272,6 +275,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
+ field = (
+ "base_tax_withholding_net_total as base_net_total"
+ if party_type == "Supplier"
+ else "base_net_total"
+ )
voucher_wise_amount = {}
vouchers = []
@@ -288,7 +296,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
)
- invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", "base_net_total"])
+ invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", field])
for d in invoices_details:
vouchers.append(d.name)
@@ -392,7 +400,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
tds_amount = 0
invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1}
- field = "sum(net_total)"
+ field = "sum(tax_withholding_net_total)"
if cint(tax_details.consider_party_ledger_amount):
invoice_filters.pop("apply_tds", None)
@@ -415,12 +423,12 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
)
supp_credit_amt += supp_jv_credit_amt
- supp_credit_amt += inv.net_total
+ supp_credit_amt += inv.tax_withholding_net_total
threshold = tax_details.get("threshold", 0)
cumulative_threshold = tax_details.get("cumulative_threshold", 0)
- if (threshold and inv.net_total >= threshold) or (
+ if (threshold and inv.tax_withholding_net_total >= threshold) or (
cumulative_threshold and supp_credit_amt >= cumulative_threshold
):
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
@@ -428,11 +436,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
):
# Get net total again as TDS is calculated on net total
# Grand is used to just check for threshold breach
- net_total = 0
- if vouchers:
- net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)")
-
- net_total += inv.net_total
+ net_total = (
+ frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)")
+ or 0.0
+ )
+ net_total += inv.tax_withholding_net_total
supp_credit_amt = net_total - cumulative_threshold
if ldc and is_valid_certificate(
@@ -440,7 +448,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
ldc.valid_upto,
inv.get("posting_date") or inv.get("transaction_date"),
tax_deducted,
- inv.net_total,
+ inv.tax_withholding_net_total,
ldc.certificate_limit,
):
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
@@ -523,7 +531,7 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net
limit_consumed = frappe.db.get_value(
"Purchase Invoice",
{"supplier": ("in", parties), "apply_tds": 1, "docstatus": 1},
- "sum(net_total)",
+ "sum(tax_withholding_net_total)",
)
if is_valid_certificate(
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index e80fe11ab30..40c732bae52 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -186,6 +186,46 @@ class TestTaxWithholdingCategory(unittest.TestCase):
for d in reversed(invoices):
d.cancel()
+ def test_tds_calculation_on_net_total_partial_tds(self):
+ frappe.db.set_value(
+ "Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS"
+ )
+ invoices = []
+
+ pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True)
+ pi.extend(
+ "items",
+ [
+ {
+ "doctype": "Purchase Invoice Item",
+ "item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
+ "qty": 1,
+ "rate": 20000,
+ "cost_center": "Main - _TC",
+ "expense_account": "Stock Received But Not Billed - _TC",
+ "apply_tds": 0,
+ },
+ {
+ "doctype": "Purchase Invoice Item",
+ "item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
+ "qty": 1,
+ "rate": 35000,
+ "cost_center": "Main - _TC",
+ "expense_account": "Stock Received But Not Billed - _TC",
+ "apply_tds": 1,
+ },
+ ],
+ )
+ pi.save()
+ pi.submit()
+ invoices.append(pi)
+
+ self.assertEqual(pi.taxes[0].tax_amount, 5500)
+
+ # cancel invoices to avoid clashing
+ for d in reversed(invoices):
+ d.cancel()
+
def test_multi_category_single_supplier(self):
frappe.db.set_value(
"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index f2ee1eb10ea..a195c575866 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -1009,7 +1009,7 @@ class ReceivablePayableReport(object):
"{range3}-{range4}".format(
range3=cint(self.filters["range3"]) + 1, range4=self.filters["range4"]
),
- "{range4}-{above}".format(range4=cint(self.filters["range4"]) + 1, above=_("Above")),
+ _("{range4}-Above").format(range4=cint(self.filters["range4"]) + 1),
]
):
self.add_column(label=label, fieldname="range" + str(i + 1))
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
index 718b6e2fcb6..5955c2e0fc9 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
@@ -75,7 +75,7 @@ frappe.query_reports["Budget Variance Report"] = {
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
- if (column.fieldname.includes('variance')) {
+ if (column.fieldname.includes(__("variance"))) {
if (data[column.fieldname] < 0) {
value = "" + value + "";
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
index 7b774ba740b..96cfab9f11f 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -383,8 +383,8 @@ def get_chart_data(filters, columns, data):
"data": {
"labels": labels,
"datasets": [
- {"name": "Budget", "chartType": "bar", "values": budget_values},
- {"name": "Actual Expense", "chartType": "bar", "values": actual_values},
+ {"name": _("Budget"), "chartType": "bar", "values": budget_values},
+ {"name": _("Actual Expense"), "chartType": "bar", "values": actual_values},
],
},
"type": "bar",
diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
index 1eb257ac853..6cc86c3efec 100644
--- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
+++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py
@@ -396,7 +396,7 @@ class Deferred_Revenue_and_Expense_Report(object):
"labels": [period.label for period in self.period_list],
"datasets": [
{
- "name": "Actual Posting",
+ "name": _("Actual Posting"),
"chartType": "bar",
"values": [x.actual for x in self.period_total],
}
@@ -410,7 +410,7 @@ class Deferred_Revenue_and_Expense_Report(object):
if self.filters.with_upcoming_postings:
chart["data"]["datasets"].append(
- {"name": "Expected", "chartType": "line", "values": [x.total for x in self.period_total]}
+ {"name": _("Expected"), "chartType": "line", "values": [x.total for x in self.period_total]}
)
return chart
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index 5512d4159d8..7e542197407 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -432,7 +432,11 @@ frappe.ui.form.on('Asset', {
set_values_from_purchase_doc: function(frm, doctype, purchase_doc) {
frm.set_value('company', purchase_doc.company);
- frm.set_value('purchase_date', purchase_doc.posting_date);
+ if (purchase_doc.bill_date) {
+ frm.set_value('purchase_date', purchase_doc.bill_date);
+ } else {
+ frm.set_value('purchase_date', purchase_doc.posting_date);
+ }
const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code);
if (!item) {
doctype_field = frappe.scrub(doctype)
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 6c1bcc7dd49..5a96131157b 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -5,7 +5,7 @@
import json
import frappe
-from frappe.tests.utils import FrappeTestCase
+from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, getdate, nowdate
from frappe.utils.data import today
@@ -709,13 +709,10 @@ class TestPurchaseOrder(FrappeTestCase):
pi.insert()
self.assertTrue(pi.get("payment_schedule"))
+ @change_settings("Accounts Settings", {"unlink_advance_payment_on_cancelation_of_order": 1})
def test_advance_payment_entry_unlink_against_purchase_order(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
- frappe.db.set_value(
- "Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 1
- )
-
po_doc = create_purchase_order()
pe = get_payment_entry("Purchase Order", po_doc.name, bank_account="_Test Bank - _TC")
@@ -735,9 +732,31 @@ class TestPurchaseOrder(FrappeTestCase):
pe_doc = frappe.get_doc("Payment Entry", pe.name)
pe_doc.cancel()
- frappe.db.set_value(
- "Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 0
- )
+ @change_settings("Accounts Settings", {"unlink_advance_payment_on_cancelation_of_order": 1})
+ def test_advance_paid_upon_payment_entry_cancellation(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+
+ po_doc = create_purchase_order()
+
+ pe = get_payment_entry("Purchase Order", po_doc.name, bank_account="_Test Bank - _TC")
+ pe.reference_no = "1"
+ pe.reference_date = nowdate()
+ pe.paid_from_account_currency = po_doc.currency
+ pe.paid_to_account_currency = po_doc.currency
+ pe.source_exchange_rate = 1
+ pe.target_exchange_rate = 1
+ pe.paid_amount = po_doc.grand_total
+ pe.save(ignore_permissions=True)
+ pe.submit()
+
+ po_doc.reload()
+ self.assertEqual(po_doc.advance_paid, po_doc.base_grand_total)
+
+ pe_doc = frappe.get_doc("Payment Entry", pe.name)
+ pe_doc.cancel()
+
+ po_doc.reload()
+ self.assertEqual(po_doc.advance_paid, 0)
def test_schedule_date(self):
po = create_purchase_order(do_not_submit=True)
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 22291a35441..216c9f45d3f 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -7,7 +7,7 @@ import json
import frappe
from frappe import _, throw
from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied
-from frappe.query_builder.functions import Sum
+from frappe.query_builder.functions import Abs, Sum
from frappe.utils import (
add_days,
add_months,
@@ -151,6 +151,7 @@ class AccountsController(TransactionBase):
self.validate_inter_company_reference()
self.disable_pricing_rule_on_internal_transfer()
+ self.disable_tax_included_prices_for_internal_transfer()
self.set_incoming_rate()
if self.meta.get_field("currency"):
@@ -226,7 +227,7 @@ class AccountsController(TransactionBase):
for item in self.get("items"):
if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
if not item.get(field_map.get(self.doctype)):
- default_deferred_account = frappe.db.get_value(
+ default_deferred_account = frappe.get_cached_value(
"Company", self.company, "default_" + field_map.get(self.doctype)
)
if not default_deferred_account:
@@ -398,6 +399,20 @@ class AccountsController(TransactionBase):
alert=1,
)
+ def disable_tax_included_prices_for_internal_transfer(self):
+ if self.is_internal_transfer():
+ tax_updated = False
+ for tax in self.get("taxes"):
+ if tax.get("included_in_print_rate"):
+ tax.included_in_print_rate = 0
+ tax_updated = True
+
+ if tax_updated:
+ frappe.msgprint(
+ _("Disabled tax included prices since this {} is an internal transfer").format(self.doctype),
+ alert=1,
+ )
+
def validate_due_date(self):
if self.get("is_pos"):
return
@@ -661,7 +676,7 @@ class AccountsController(TransactionBase):
def validate_enabled_taxes_and_charges(self):
taxes_and_charges_doctype = self.meta.get_options("taxes_and_charges")
- if frappe.db.get_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"):
+ if frappe.get_cached_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"):
frappe.throw(
_("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges)
)
@@ -669,7 +684,7 @@ class AccountsController(TransactionBase):
def validate_tax_account_company(self):
for d in self.get("taxes"):
if d.account_head:
- tax_account_company = frappe.db.get_value("Account", d.account_head, "company")
+ tax_account_company = frappe.get_cached_value("Account", d.account_head, "company")
if tax_account_company != self.company:
frappe.throw(
_("Row #{0}: Account {1} does not belong to company {2}").format(
@@ -804,15 +819,12 @@ class AccountsController(TransactionBase):
self.set("advances", [])
advance_allocated = 0
for d in res:
- if d.against_order:
- allocated_amount = flt(d.amount)
+ if self.get("party_account_currency") == self.company_currency:
+ amount = self.get("base_rounded_total") or self.base_grand_total
else:
- if self.get("party_account_currency") == self.company_currency:
- amount = self.get("base_rounded_total") or self.base_grand_total
- else:
- amount = self.get("rounded_total") or self.grand_total
+ amount = self.get("rounded_total") or self.grand_total
- allocated_amount = min(amount - advance_allocated, d.amount)
+ allocated_amount = min(amount - advance_allocated, d.amount)
advance_allocated += flt(allocated_amount)
advance_row = {
@@ -917,7 +929,9 @@ class AccountsController(TransactionBase):
party_account = self.credit_to if is_purchase_invoice else self.debit_to
party_type = "Supplier" if is_purchase_invoice else "Customer"
- gain_loss_account = frappe.db.get_value("Company", self.company, "exchange_gain_loss_account")
+ gain_loss_account = frappe.get_cached_value(
+ "Company", self.company, "exchange_gain_loss_account"
+ )
if not gain_loss_account:
frappe.throw(
_("Please set default Exchange Gain/Loss Account in Company {}").format(self.get("company"))
@@ -1014,7 +1028,7 @@ class AccountsController(TransactionBase):
else self.grand_total
),
"outstanding_amount": self.outstanding_amount,
- "difference_account": frappe.db.get_value(
+ "difference_account": frappe.get_cached_value(
"Company", self.company, "exchange_gain_loss_account"
),
"exchange_gain_loss": flt(d.get("exchange_gain_loss")),
@@ -1334,30 +1348,20 @@ class AccountsController(TransactionBase):
return stock_items
def set_total_advance_paid(self):
- if self.doctype == "Sales Order":
- dr_or_cr = "credit_in_account_currency"
- rev_dr_or_cr = "debit_in_account_currency"
- party = self.customer
- else:
- dr_or_cr = "debit_in_account_currency"
- rev_dr_or_cr = "credit_in_account_currency"
- party = self.supplier
-
- advance = frappe.db.sql(
- """
- select
- account_currency, sum({dr_or_cr}) - sum({rev_dr_cr}) as amount
- from
- `tabGL Entry`
- where
- against_voucher_type = %s and against_voucher = %s and party=%s
- and docstatus = 1
- """.format(
- dr_or_cr=dr_or_cr, rev_dr_cr=rev_dr_or_cr
- ),
- (self.doctype, self.name, party),
- as_dict=1,
- ) # nosec
+ ple = frappe.qb.DocType("Payment Ledger Entry")
+ party = self.customer if self.doctype == "Sales Order" else self.supplier
+ advance = (
+ frappe.qb.from_(ple)
+ .select(ple.account_currency, Abs(Sum(ple.amount)).as_("amount"))
+ .where(
+ (ple.against_voucher_type == self.doctype)
+ & (ple.against_voucher_no == self.name)
+ & (ple.party == party)
+ & (ple.delinked == 0)
+ & (ple.company == self.company)
+ )
+ .run(as_dict=True)
+ )
if advance:
advance = advance[0]
@@ -1392,7 +1396,7 @@ class AccountsController(TransactionBase):
@property
def company_abbr(self):
if not hasattr(self, "_abbr"):
- self._abbr = frappe.db.get_value("Company", self.company, "abbr")
+ self._abbr = frappe.get_cached_value("Company", self.company, "abbr")
return self._abbr
@@ -1778,7 +1782,7 @@ class AccountsController(TransactionBase):
"""
if self.is_internal_transfer() and not self.unrealized_profit_loss_account:
- unrealized_profit_loss_account = frappe.db.get_value(
+ unrealized_profit_loss_account = frappe.get_cached_value(
"Company", self.company, "unrealized_profit_loss_account"
)
@@ -1893,7 +1897,9 @@ class AccountsController(TransactionBase):
@frappe.whitelist()
def get_tax_rate(account_head):
- return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
+ return frappe.get_cached_value(
+ "Account", account_head, ["tax_rate", "account_name"], as_dict=True
+ )
@frappe.whitelist()
@@ -1902,7 +1908,7 @@ def get_default_taxes_and_charges(master_doctype, tax_template=None, company=Non
return {}
if tax_template and company:
- tax_template_company = frappe.db.get_value(master_doctype, tax_template, "company")
+ tax_template_company = frappe.get_cached_value(master_doctype, tax_template, "company")
if tax_template_company == company:
return
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 04a0dfa3d40..39ef68a9fb5 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -326,7 +326,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
company = frappe.db.get_value("Delivery Note", source_name, "company")
- default_warehouse_for_sales_return = frappe.db.get_value(
+ default_warehouse_for_sales_return = frappe.get_cached_value(
"Company", company, "default_warehouse_for_sales_return"
)
@@ -340,11 +340,11 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
# look for Print Heading "Credit Note"
if not doc.select_print_heading:
- doc.select_print_heading = frappe.db.get_value("Print Heading", _("Credit Note"))
+ doc.select_print_heading = frappe.get_cached_value("Print Heading", _("Credit Note"))
elif doctype == "Purchase Invoice":
# look for Print Heading "Debit Note"
- doc.select_print_heading = frappe.db.get_value("Print Heading", _("Debit Note"))
+ doc.select_print_heading = frappe.get_cached_value("Print Heading", _("Debit Note"))
for tax in doc.get("taxes") or []:
if tax.charge_type == "Actual":
@@ -503,7 +503,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
doctype
+ " Item": {
"doctype": doctype + " Item",
- "field_map": {"serial_no": "serial_no", "batch_no": "batch_no"},
+ "field_map": {"serial_no": "serial_no", "batch_no": "batch_no", "bom": "bom"},
"postprocess": update_item,
},
"Payment Schedule": {"doctype": "Payment Schedule", "postprocess": update_terms},
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 98dc58677b9..1e4fabe0d26 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -57,7 +57,7 @@ class StockController(AccountsController):
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
provisional_accounting_for_non_stock_items = cint(
- frappe.db.get_value(
+ frappe.get_cached_value(
"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
)
)
@@ -200,7 +200,7 @@ class StockController(AccountsController):
elif self.get("is_internal_supplier"):
warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"]
- expense_account = frappe.db.get_value("Company", self.company, "default_expense_account")
+ expense_account = frappe.get_cached_value("Company", self.company, "default_expense_account")
gl_list.append(
self.get_gl_dict(
@@ -235,7 +235,7 @@ class StockController(AccountsController):
if warehouse_with_no_account:
for wh in warehouse_with_no_account:
- if frappe.db.get_value("Warehouse", wh, "company"):
+ if frappe.get_cached_value("Warehouse", wh, "company"):
frappe.throw(
_(
"Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}."
@@ -449,15 +449,15 @@ class StockController(AccountsController):
# Get value based on doctype name
if not sl_dict.get(dimension.target_fieldname):
- fieldname = frappe.get_cached_value(
- "DocField", {"parent": self.doctype, "options": dimension.fetch_from_parent}, "fieldname"
+ fieldname = next(
+ (
+ field.fieldname
+ for field in frappe.get_meta(self.doctype).fields
+ if field.options == dimension.fetch_from_parent
+ ),
+ None,
)
- if not fieldname:
- fieldname = frappe.get_cached_value(
- "Custom Field", {"dt": self.doctype, "options": dimension.fetch_from_parent}, "fieldname"
- )
-
if fieldname and self.get(fieldname):
sl_dict[dimension.target_fieldname] = self.get(fieldname)
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index aa4468c04e4..7819fa586ca 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -89,6 +89,9 @@ class SubcontractingController(StockController):
if bom.item != item.item_code:
msg = f"Please select an valid BOM for Item {item.item_name}."
frappe.throw(_(msg))
+ else:
+ msg = f"Please select a BOM for Item {item.item_name}."
+ frappe.throw(_(msg))
def __get_data_before_save(self):
item_dict = {}
@@ -97,7 +100,7 @@ class SubcontractingController(StockController):
and self._doc_before_save
):
for row in self._doc_before_save.get("items"):
- item_dict[row.name] = (row.item_code, row.qty)
+ item_dict[row.name] = (row.item_code, row.received_qty or row.qty)
return item_dict
@@ -115,7 +118,9 @@ class SubcontractingController(StockController):
for row in self.items:
self.__reference_name.append(row.name)
- if (row.name not in item_dict) or (row.item_code, row.qty) != item_dict[row.name]:
+ if (row.name not in item_dict) or (row.item_code, row.received_qty or row.qty) != item_dict[
+ row.name
+ ]:
self.__changed_name.append(row.name)
if item_dict.get(row.name):
@@ -458,12 +463,13 @@ class SubcontractingController(StockController):
def __get_qty_based_on_material_transfer(self, item_row, transfer_item):
key = (item_row.item_code, item_row.get(self.subcontract_data.order_field))
+ item_qty = item_row.received_qty or item_row.qty
- if self.qty_to_be_received == item_row.qty:
+ if self.qty_to_be_received.get(key) == item_qty:
return transfer_item.qty
if self.qty_to_be_received:
- qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0))
+ qty = (flt(item_qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0))
transfer_item.item_details.required_qty = transfer_item.qty
if transfer_item.serial_no or frappe.get_cached_value(
@@ -488,7 +494,11 @@ class SubcontractingController(StockController):
for bom_item in self.__get_materials_from_bom(
row.item_code, row.bom, row.get("include_exploded_items")
):
- qty = flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor
+ qty = (
+ flt(bom_item.qty_consumed_per_unit)
+ * flt(row.received_qty or row.qty)
+ * row.conversion_factor
+ )
bom_item.main_item_code = row.item_code
self.__update_reserve_warehouse(bom_item, row)
self.__set_alternative_item(bom_item)
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index b5836c9070a..c6a634ba806 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -58,12 +58,25 @@ class calculate_taxes_and_totals(object):
self.initialize_taxes()
self.determine_exclusive_rate()
self.calculate_net_total()
+ self.calculate_tax_withholding_net_total()
self.calculate_taxes()
self.manipulate_grand_total_for_inclusive_tax()
self.calculate_totals()
self._cleanup()
self.calculate_total_net_weight()
+ def calculate_tax_withholding_net_total(self):
+ if hasattr(self.doc, "tax_withholding_net_total"):
+ sum_net_amount = 0
+ sum_base_net_amount = 0
+ for item in self.doc.get("items"):
+ if hasattr(item, "apply_tds") and item.apply_tds:
+ sum_net_amount += item.net_amount
+ sum_base_net_amount += item.base_net_amount
+
+ self.doc.tax_withholding_net_total = sum_net_amount
+ self.doc.base_tax_withholding_net_total = sum_base_net_amount
+
def validate_item_tax_template(self):
for item in self.doc.get("items"):
if item.item_code and item.get("item_tax_template"):
@@ -1043,7 +1056,7 @@ class init_landed_taxes_and_totals(object):
company_currency = erpnext.get_company_currency(self.doc.company)
for d in self.doc.get(self.tax_field):
if not d.account_currency:
- account_currency = frappe.db.get_value("Account", d.expense_account, "account_currency")
+ account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
d.account_currency = account_currency or company_currency
def set_exchange_rate(self):
diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py
index 8490d145286..0e6fe95d45f 100644
--- a/erpnext/controllers/tests/test_subcontracting_controller.py
+++ b/erpnext/controllers/tests/test_subcontracting_controller.py
@@ -815,6 +815,7 @@ def add_second_row_in_scr(scr):
"item_name",
"qty",
"uom",
+ "bom",
"warehouse",
"stock_uom",
"subcontracting_order",
diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py
index 1d6c5dc0be0..1fb722e1123 100644
--- a/erpnext/controllers/trends.py
+++ b/erpnext/controllers/trends.py
@@ -80,7 +80,7 @@ def get_data(filters, conditions):
if conditions.get("trans") == "Quotation" and filters.get("group_by") == "Customer":
cond += " and t1.quotation_to = 'Customer'"
- year_start_date, year_end_date = frappe.db.get_value(
+ year_start_date, year_end_date = frappe.get_cached_value(
"Fiscal Year", filters.get("fiscal_year"), ["year_start_date", "year_end_date"]
)
@@ -275,7 +275,7 @@ def get_period_date_ranges(period, fiscal_year=None, year_start_date=None):
from dateutil.relativedelta import relativedelta
if not year_start_date:
- year_start_date, year_end_date = frappe.db.get_value(
+ year_start_date, year_end_date = frappe.get_cached_value(
"Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]
)
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 4aab3fa3734..62476182485 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -589,66 +589,69 @@ erpnext.work_order = {
}
}
- if(!frm.doc.skip_transfer){
+ if (frm.doc.status != 'Stopped') {
// If "Material Consumption is check in Manufacturing Settings, allow Material Consumption
- if (flt(doc.material_transferred_for_manufacturing) > 0 && frm.doc.status != 'Stopped') {
- if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) {
- frm.has_finish_btn = true;
-
- if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) {
- // Only show "Material Consumption" when required_qty > consumed_qty
- var counter = 0;
- var tbl = frm.doc.required_items || [];
- var tbl_lenght = tbl.length;
- for (var i = 0, len = tbl_lenght; i < len; i++) {
- let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty;
- if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
- counter += 1;
- }
- }
- if (counter > 0) {
- var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() {
- const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on;
- erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on);
- });
- consumption_btn.addClass('btn-primary');
+ if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) {
+ if (flt(doc.material_transferred_for_manufacturing) > 0 || frm.doc.skip_transfer) {
+ // Only show "Material Consumption" when required_qty > consumed_qty
+ var counter = 0;
+ var tbl = frm.doc.required_items || [];
+ var tbl_lenght = tbl.length;
+ for (var i = 0, len = tbl_lenght; i < len; i++) {
+ let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty;
+ if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
+ counter += 1;
}
}
+ if (counter > 0) {
+ var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() {
+ const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on;
+ erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on);
+ });
+ consumption_btn.addClass('btn-primary');
+ }
+ }
+ }
+ if(!frm.doc.skip_transfer){
+ if (flt(doc.material_transferred_for_manufacturing) > 0) {
+ if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) {
+ frm.has_finish_btn = true;
+
+ var finish_btn = frm.add_custom_button(__('Finish'), function() {
+ erpnext.work_order.make_se(frm, 'Manufacture');
+ });
+
+ if(doc.material_transferred_for_manufacturing>=doc.qty) {
+ // all materials transferred for manufacturing, make this primary
+ finish_btn.addClass('btn-primary');
+ }
+ } else {
+ frappe.db.get_doc("Manufacturing Settings").then((doc) => {
+ let allowance_percentage = doc.overproduction_percentage_for_work_order;
+
+ if (allowance_percentage > 0) {
+ let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty);
+
+ if ((flt(doc.produced_qty) < allowed_qty)) {
+ frm.add_custom_button(__('Finish'), function() {
+ erpnext.work_order.make_se(frm, 'Manufacture');
+ });
+ }
+ }
+ });
+ }
+ }
+ } else {
+ if ((flt(doc.produced_qty) < flt(doc.qty))) {
var finish_btn = frm.add_custom_button(__('Finish'), function() {
erpnext.work_order.make_se(frm, 'Manufacture');
});
-
- if(doc.material_transferred_for_manufacturing>=doc.qty) {
- // all materials transferred for manufacturing, make this primary
- finish_btn.addClass('btn-primary');
- }
- } else {
- frappe.db.get_doc("Manufacturing Settings").then((doc) => {
- let allowance_percentage = doc.overproduction_percentage_for_work_order;
-
- if (allowance_percentage > 0) {
- let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty);
-
- if ((flt(doc.produced_qty) < allowed_qty)) {
- frm.add_custom_button(__('Finish'), function() {
- erpnext.work_order.make_se(frm, 'Manufacture');
- });
- }
- }
- });
+ finish_btn.addClass('btn-primary');
}
}
- } else {
- if ((flt(doc.produced_qty) < flt(doc.qty)) && frm.doc.status != 'Stopped') {
- var finish_btn = frm.add_custom_button(__('Finish'), function() {
- erpnext.work_order.make_se(frm, 'Manufacture');
- });
- finish_btn.addClass('btn-primary');
- }
}
}
-
},
calculate_cost: function(doc) {
if (doc.operations){
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 5ed8f3f4a91..8e726bf0c0a 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -317,4 +317,4 @@ erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries
erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization
erpnext.patches.v13_0.update_schedule_type_in_loans
-
+erpnext.patches.v14_0.update_tds_fields
\ No newline at end of file
diff --git a/erpnext/patches/v14_0/update_tds_fields.py b/erpnext/patches/v14_0/update_tds_fields.py
new file mode 100644
index 00000000000..a333c5d7a50
--- /dev/null
+++ b/erpnext/patches/v14_0/update_tds_fields.py
@@ -0,0 +1,29 @@
+import frappe
+from frappe.utils import nowdate
+
+from erpnext.accounts.utils import FiscalYearError, get_fiscal_year
+
+
+def execute():
+ # Only do for current fiscal year, no need to repost for all years
+ for company in frappe.get_all("Company"):
+ try:
+ fiscal_year_details = get_fiscal_year(date=nowdate(), company=company.name, as_dict=True)
+
+ purchase_invoice = frappe.qb.DocType("Purchase Invoice")
+
+ frappe.qb.update(purchase_invoice).set(
+ purchase_invoice.tax_withholding_net_total, purchase_invoice.net_total
+ ).set(
+ purchase_invoice.base_tax_withholding_net_total, purchase_invoice.base_net_total
+ ).where(
+ purchase_invoice.company == company.name
+ ).where(
+ purchase_invoice.apply_tds == 1
+ ).where(
+ purchase_invoice.posting_date >= fiscal_year_details.year_start_date
+ ).where(
+ purchase_invoice.docstatus == 1
+ ).run()
+ except FiscalYearError:
+ pass
diff --git a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
index 5bb58faf2fc..9ef8ce6b63e 100644
--- a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
+++ b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
@@ -30,28 +30,28 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
get_dt_columns() {
this.columns = [
{
- name: "Date",
+ name: __("Date"),
editable: false,
width: 100,
},
{
- name: "Party Type",
+ name: __("Party Type"),
editable: false,
width: 95,
},
{
- name: "Party",
+ name: __("Party"),
editable: false,
width: 100,
},
{
- name: "Description",
+ name: __("Description"),
editable: false,
width: 350,
},
{
- name: "Deposit",
+ name: __("Deposit"),
editable: false,
width: 100,
format: (value) =>
@@ -60,7 +60,7 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
"",
},
{
- name: "Withdrawal",
+ name: __("Withdrawal"),
editable: false,
width: 100,
format: (value) =>
@@ -69,26 +69,26 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
"",
},
{
- name: "Unallocated Amount",
+ name: __("Unallocated Amount"),
editable: false,
width: 100,
format: (value) =>
- "" +
+ "" +
format_currency(value, this.currency) +
"",
},
{
- name: "Reference Number",
+ name: __("Reference Number"),
editable: false,
width: 140,
},
{
- name: "Actions",
+ name: __("Actions"),
editable: false,
sortable: false,
focusable: false,
dropdown: false,
- width: 80,
+ width: 100,
},
];
}
@@ -118,7 +118,7 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
row["reference_number"],
`