Merge pull request #43892 from frappe/version-14-hotfix

chore: release v14
This commit is contained in:
ruthra kumar
2024-10-30 15:21:35 +05:30
committed by GitHub
24 changed files with 182 additions and 75 deletions

View File

@@ -153,7 +153,7 @@ class JournalEntry(AccountsController):
frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation")) frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation"))
def validate_stock_accounts(self): def validate_stock_accounts(self):
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name) stock_accounts = get_stock_accounts(self.company, accounts=self.accounts)
for account in stock_accounts: for account in stock_accounts:
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance( account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
account, self.posting_date, self.company account, self.posting_date, self.company

View File

@@ -146,12 +146,12 @@ class TestJournalEntry(unittest.TestCase):
"credit_in_account_currency": 0 if diff > 0 else abs(diff), "credit_in_account_currency": 0 if diff > 0 else abs(diff),
}, },
) )
jv.insert()
if account_bal == stock_bal: if account_bal == stock_bal:
self.assertRaises(StockAccountInvalidTransaction, jv.submit) self.assertRaises(StockAccountInvalidTransaction, jv.insert)
frappe.db.rollback() frappe.db.rollback()
else: else:
jv.insert()
jv.submit() jv.submit()
jv.cancel() jv.cancel()

View File

@@ -256,6 +256,7 @@ class PaymentReconciliation(Document):
"posting_date": inv.posting_date, "posting_date": inv.posting_date,
"currency": inv.currency, "currency": inv.currency,
"cost_center": inv.cost_center, "cost_center": inv.cost_center,
"remarks": inv.remarks,
} }
) )
) )

View File

@@ -14,7 +14,7 @@
"amount", "amount",
"difference_amount", "difference_amount",
"sec_break1", "sec_break1",
"remark", "remarks",
"currency", "currency",
"exchange_rate", "exchange_rate",
"cost_center" "cost_center"
@@ -74,12 +74,6 @@
"fieldname": "sec_break1", "fieldname": "sec_break1",
"fieldtype": "Section Break" "fieldtype": "Section Break"
}, },
{
"fieldname": "remark",
"fieldtype": "Small Text",
"label": "Remark",
"read_only": 1
},
{ {
"fieldname": "currency", "fieldname": "currency",
"fieldtype": "Link", "fieldtype": "Link",
@@ -105,12 +99,18 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Cost Center", "label": "Cost Center",
"options": "Cost Center" "options": "Cost Center"
},
{
"fieldname": "remarks",
"fieldtype": "Small Text",
"label": "Remarks",
"read_only": 1
} }
], ],
"is_virtual": 1, "is_virtual": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2023-11-17 17:33:34.818530", "modified": "2024-10-29 16:24:43.021230",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Reconciliation Payment", "name": "Payment Reconciliation Payment",

View File

@@ -238,12 +238,12 @@ class PaymentRequest(Document):
return controller.get_payment_url( return controller.get_payment_url(
**{ **{
"amount": flt(self.grand_total, self.precision("grand_total")), "amount": flt(self.grand_total, self.precision("grand_total")),
"title": data.company.encode("utf-8"), "title": data.company,
"description": self.subject.encode("utf-8"), "description": self.subject,
"reference_doctype": "Payment Request", "reference_doctype": "Payment Request",
"reference_docname": self.name, "reference_docname": self.name,
"payer_email": self.email_to or frappe.session.user, "payer_email": self.email_to or frappe.session.user,
"payer_name": frappe.safe_encode(data.customer_name), "payer_name": data.customer_name,
"order_id": self.name, "order_id": self.name,
"currency": self.currency, "currency": self.currency,
} }

View File

@@ -25,6 +25,13 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
} }
}; };
}); });
this.frm.set_query("expense_account", "items", function () {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: { company: doc.company },
};
});
} }
onload() { onload() {
@@ -303,8 +310,11 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
party_type: "Supplier", party_type: "Supplier",
account: this.frm.doc.credit_to, account: this.frm.doc.credit_to,
price_list: this.frm.doc.buying_price_list, price_list: this.frm.doc.buying_price_list,
fetch_payment_terms_template: cint(!this.frm.doc.ignore_default_payment_terms_template) fetch_payment_terms_template: cint(
}, function() { (this.frm.doc.is_return == 0) & !this.frm.doc.ignore_default_payment_terms_template
),
},
function () {
me.apply_pricing_rule(); me.apply_pricing_rule();
me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0; me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0;
me.frm.doc.tax_withholding_category = me.frm.supplier_tds; me.frm.doc.tax_withholding_category = me.frm.supplier_tds;
@@ -469,13 +479,6 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn)
} }
} }
cur_frm.set_query("expense_account", "items", function(doc) {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: {'company': doc.company }
}
});
cur_frm.set_query("wip_composite_asset", "items", function() { cur_frm.set_query("wip_composite_asset", "items", function() {
return { return {
filters: {'is_composite_asset': 1, 'docstatus': 0 } filters: {'is_composite_asset': 1, 'docstatus': 0 }

View File

@@ -1128,12 +1128,14 @@
"label": "Payment Terms" "label": "Payment Terms"
}, },
{ {
"depends_on": "eval:(!doc.is_paid && !doc.is_return)",
"fieldname": "payment_terms_template", "fieldname": "payment_terms_template",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Payment Terms Template", "label": "Payment Terms Template",
"options": "Payment Terms Template" "options": "Payment Terms Template"
}, },
{ {
"depends_on": "eval:(!doc.is_paid && !doc.is_return)",
"fieldname": "payment_schedule", "fieldname": "payment_schedule",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Payment Schedule", "label": "Payment Schedule",
@@ -1611,7 +1613,7 @@
"idx": 204, "idx": 204,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2024-09-11 12:59:19.130593", "modified": "2024-10-25 18:13:01.944477",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@@ -280,8 +280,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
party_type: "Customer", party_type: "Customer",
account: this.frm.doc.debit_to, account: this.frm.doc.debit_to,
price_list: this.frm.doc.selling_price_list, price_list: this.frm.doc.selling_price_list,
pos_profile: pos_profile pos_profile: pos_profile,
}, function() { fetch_payment_terms_template: cint(
(this.frm.doc.is_return == 0) & !this.frm.doc.ignore_default_payment_terms_template
),
},
function () {
me.apply_pricing_rule(); me.apply_pricing_rule();
}); });

View File

@@ -482,7 +482,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
payment_entry_filters.pop("apply_tax_withholding_amount", None) payment_entry_filters.pop("apply_tax_withholding_amount", None)
payment_entry_filters.pop("tax_withholding_category", None) payment_entry_filters.pop("tax_withholding_category", None)
supp_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0 supp_inv_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0
supp_jv_credit_amt = ( supp_jv_credit_amt = (
frappe.db.get_value( frappe.db.get_value(
@@ -506,7 +506,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
group_by="payment_type", group_by="payment_type",
) )
supp_credit_amt += supp_jv_credit_amt supp_credit_amt = supp_jv_credit_amt
supp_credit_amt += inv.tax_withholding_net_total supp_credit_amt += inv.tax_withholding_net_total
for type in payment_entry_amounts: for type in payment_entry_amounts:
@@ -524,18 +524,18 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
tax_withholding_net_total = inv.tax_withholding_net_total tax_withholding_net_total = inv.tax_withholding_net_total
if (threshold and tax_withholding_net_total >= threshold) or ( if (threshold and tax_withholding_net_total >= threshold) or (
cumulative_threshold and supp_credit_amt >= cumulative_threshold cumulative_threshold and (supp_credit_amt + supp_inv_credit_amt) >= cumulative_threshold
): ):
# Get net total again as TDS is calculated on net total
# Grand is used to just check for threshold breach
net_total = (
frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)") or 0.0
)
supp_credit_amt += net_total
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint( if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
tax_details.tax_on_excess_amount tax_details.tax_on_excess_amount
): ):
# Get net total again as TDS is calculated on net total
# Grand is used to just check for threshold breach
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 supp_credit_amt = net_total - cumulative_threshold
if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0): if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0):

View File

@@ -121,6 +121,46 @@ class TestTaxWithholdingCategory(FrappeTestCase):
for d in reversed(invoices): for d in reversed(invoices):
d.cancel() d.cancel()
def test_cumulative_threshold_with_party_ledger_amount_on_net_total(self):
invoices = []
frappe.db.set_value(
"Supplier", "Test TDS Supplier3", "tax_withholding_category", "Advance TDS Category"
)
# Invoice with tax and without exceeding single and cumulative thresholds
for _ in range(2):
pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=1000, do_not_save=True)
pi.apply_tds = 1
pi.append(
"taxes",
{
"category": "Total",
"charge_type": "Actual",
"account_head": "_Test Account VAT - _TC",
"cost_center": "Main - _TC",
"tax_amount": 500,
"description": "Test",
"add_deduct_tax": "Add",
},
)
pi.save()
pi.submit()
invoices.append(pi)
# Third Invoice exceeds single threshold and not exceeding cumulative threshold
pi1 = create_purchase_invoice(supplier="Test TDS Supplier3", rate=6000)
pi1.apply_tds = 1
pi1.save()
pi1.submit()
invoices.append(pi1)
# Cumulative threshold is 10,000
# Threshold calculation should be only on the third invoice
self.assertEqual(pi1.taxes[0].tax_amount, 800)
for d in reversed(invoices):
d.cancel()
def test_cumulative_threshold_tcs(self): def test_cumulative_threshold_tcs(self):
frappe.db.set_value( frappe.db.set_value(
"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS" "Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"

View File

@@ -500,7 +500,8 @@ class ReceivablePayableReport:
from `tab{row.voucher_type}` si, `tabPayment Schedule` ps from `tab{row.voucher_type}` si, `tabPayment Schedule` ps
where where
si.name = ps.parent and si.name = ps.parent and
si.name = %s si.name = %s and
si.is_return = 0
order by ps.paid_amount desc, due_date order by ps.paid_amount desc, due_date
""", """,
row.voucher_no, row.voucher_no,

View File

@@ -337,9 +337,17 @@ def get_accounts_with_children(accounts):
return list(set(all_accounts)) if all_accounts else None return list(set(all_accounts)) if all_accounts else None
def set_bill_no(gl_entries):
inv_details = get_supplier_invoice_details()
for gl in gl_entries:
gl["bill_no"] = inv_details.get(gl.get("against_voucher"), "")
def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries): def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries):
data = [] data = []
set_bill_no(gl_entries)
gle_map = initialize_gle_map(gl_entries, filters) gle_map = initialize_gle_map(gl_entries, filters)
totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map) totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map)
@@ -513,7 +521,6 @@ def get_account_type_map(company):
def get_result_as_list(data, filters): def get_result_as_list(data, filters):
balance, _balance_in_account_currency = 0, 0 balance, _balance_in_account_currency = 0, 0
inv_details = get_supplier_invoice_details()
for d in data: for d in data:
if not d.get("posting_date"): if not d.get("posting_date"):
@@ -523,7 +530,6 @@ def get_result_as_list(data, filters):
d["balance"] = balance d["balance"] = balance
d["account_currency"] = filters.account_currency d["account_currency"] = filters.account_currency
d["bill_no"] = inv_details.get(d.get("against_voucher"), "")
return data return data

View File

@@ -522,7 +522,8 @@ def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts, inclu
tax_details = frappe.db.sql( tax_details = frappe.db.sql(
"""select parent, account_head, """select parent, account_head,
sum(base_tax_amount_after_discount_amount) as tax_amount sum(base_tax_amount_after_discount_amount) as tax_amount
from `tabSales Taxes and Charges` where parent in (%s) group by parent, account_head""" from `tabSales Taxes and Charges` where parent in (%s) and parenttype = 'Sales Invoice'
group by parent, account_head"""
% ", ".join(["%s"] * len(invoice_list)), % ", ".join(["%s"] * len(invoice_list)),
tuple(inv.name for inv in invoice_list), tuple(inv.name for inv in invoice_list),
as_dict=1, as_dict=1,

View File

@@ -1524,12 +1524,16 @@ def compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
return matched return matched
def get_stock_accounts(company, voucher_type=None, voucher_no=None): def get_stock_accounts(company, voucher_type=None, voucher_no=None, accounts=None):
stock_accounts = [ stock_accounts = [
d.name d.name
for d in frappe.db.get_all("Account", {"account_type": "Stock", "company": company, "is_group": 0}) for d in frappe.db.get_all("Account", {"account_type": "Stock", "company": company, "is_group": 0})
] ]
if voucher_type and voucher_no:
if accounts:
stock_accounts = [row.account for row in accounts if row.account in stock_accounts]
elif voucher_type and voucher_no:
if voucher_type == "Journal Entry": if voucher_type == "Journal Entry":
stock_accounts = [ stock_accounts = [
d.account d.account
@@ -1926,6 +1930,7 @@ class QueryPaymentLedger:
ple.cost_center.as_("cost_center"), ple.cost_center.as_("cost_center"),
Sum(ple.amount).as_("amount"), Sum(ple.amount).as_("amount"),
Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"), Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"),
ple.remarks,
) )
.where(ple.delinked == 0) .where(ple.delinked == 0)
.where(Criterion.all(filter_on_voucher_no)) .where(Criterion.all(filter_on_voucher_no))
@@ -1988,6 +1993,7 @@ class QueryPaymentLedger:
Table("vouchers").due_date, Table("vouchers").due_date,
Table("vouchers").currency, Table("vouchers").currency,
Table("vouchers").cost_center.as_("cost_center"), Table("vouchers").cost_center.as_("cost_center"),
Table("vouchers").remarks,
) )
.where(Criterion.all(filter_on_outstanding_amount)) .where(Criterion.all(filter_on_outstanding_amount))
) )

View File

@@ -137,8 +137,8 @@ frappe.ui.form.on("Request for Quotation",{
return; return;
} }
}, },
"Download PDF for Supplier", __("Download PDF for Supplier"),
"Download" __("Download")
); );
}, },
__("Tools") __("Tools")
@@ -257,8 +257,10 @@ frappe.ui.form.on("Request for Quotation",{
}); });
}; };
dialog.fields_dict.note.$wrapper.append(`<p class="small text-muted">This is a preview of the email to be sent. A PDF of the document will const msg = __(
automatically be attached with the email.</p>`); "This is a preview of the email to be sent. A PDF of the document will automatically be attached with the email."
);
dialog.fields_dict.note.$wrapper.append(`<p class="small text-muted">${msg}</p>`);
dialog.show(); dialog.show();
} }

View File

@@ -415,6 +415,11 @@ class AccountsController(TransactionBase):
) )
def validate_invoice_documents_schedule(self): def validate_invoice_documents_schedule(self):
if self.is_return:
self.payment_terms_template = ""
self.payment_schedule = []
return
self.validate_payment_schedule_dates() self.validate_payment_schedule_dates()
self.set_due_date() self.set_due_date()
self.set_payment_schedule() self.set_payment_schedule()
@@ -429,7 +434,7 @@ class AccountsController(TransactionBase):
self.validate_payment_schedule_amount() self.validate_payment_schedule_amount()
def validate_all_documents_schedule(self): def validate_all_documents_schedule(self):
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return: if self.doctype in ("Sales Invoice", "Purchase Invoice"):
self.validate_invoice_documents_schedule() self.validate_invoice_documents_schedule()
elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"): elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"):
self.validate_non_invoice_documents_schedule() self.validate_non_invoice_documents_schedule()
@@ -3308,6 +3313,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
parent.update_billing_percentage() parent.update_billing_percentage()
parent.set_status() parent.set_status()
parent.validate_uom_is_integer("uom", "qty")
parent.validate_uom_is_integer("stock_uom", "stock_qty")
def check_if_child_table_updated(child_table_before_update, child_table_after_update, fields_to_check): def check_if_child_table_updated(child_table_before_update, child_table_after_update, fields_to_check):
accounting_dimensions = [*get_accounting_dimensions(), "cost_center", "project"] accounting_dimensions = [*get_accounting_dimensions(), "cost_center", "project"]

View File

@@ -78,6 +78,9 @@ def validate_returned_items(doc):
if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]: if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]:
select_fields += ",rejected_qty, received_qty" select_fields += ",rejected_qty, received_qty"
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
select_fields += ",name"
for d in frappe.db.sql( for d in frappe.db.sql(
f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""", f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""",
doc.return_against, doc.return_against,
@@ -103,15 +106,24 @@ def validate_returned_items(doc):
items_returned = False items_returned = False
for d in doc.get("items"): for d in doc.get("items"):
key = d.item_code
raise_exception = False
if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]:
field = frappe.scrub(doc.doctype) + "_item"
if d.get(field):
key = (d.item_code, d.get(field))
raise_exception = True
if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0): if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0):
if d.item_code not in valid_items: if key not in valid_items:
frappe.throw( frappe.msgprint(
_("Row # {0}: Returned Item {1} does not exist in {2} {3}").format( _("Row # {0}: Returned Item {1} does not exist in {2} {3}").format(
d.idx, d.item_code, doc.doctype, doc.return_against d.idx, d.item_code, doc.doctype, doc.return_against
) ),
raise_exception=raise_exception,
) )
else: else:
ref = valid_items.get(d.item_code, frappe._dict()) ref = valid_items.get(key, frappe._dict())
validate_quantity(doc, d, ref, valid_items, already_returned_items) validate_quantity(doc, d, ref, valid_items, already_returned_items)
if ( if (
@@ -206,8 +218,12 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
def get_ref_item_dict(valid_items, ref_item_row): def get_ref_item_dict(valid_items, ref_item_row):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
key = ref_item_row.item_code
if ref_item_row.get("name"):
key = (ref_item_row.item_code, ref_item_row.name)
valid_items.setdefault( valid_items.setdefault(
ref_item_row.item_code, key,
frappe._dict( frappe._dict(
{ {
"qty": 0, "qty": 0,
@@ -221,7 +237,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
} }
), ),
) )
item_dict = valid_items[ref_item_row.item_code] item_dict = valid_items[key]
item_dict["qty"] += ref_item_row.qty item_dict["qty"] += ref_item_row.qty
item_dict["stock_qty"] += ref_item_row.get("stock_qty", 0) item_dict["stock_qty"] += ref_item_row.get("stock_qty", 0)
if ref_item_row.get("rate", 0) > item_dict["rate"]: if ref_item_row.get("rate", 0) > item_dict["rate"]:

View File

@@ -767,26 +767,19 @@ class JobCard(Document):
qty = 0 qty = 0
if self.work_order: if self.work_order:
doc = frappe.get_doc("Work Order", self.work_order)
if doc.transfer_material_against == "Job Card" and not doc.skip_transfer: if doc.transfer_material_against == "Job Card" and not doc.skip_transfer:
completed = True min_qty = []
for d in doc.operations: for d in doc.operations:
if d.status != "Completed": if d.completed_qty:
completed = False min_qty.append(d.completed_qty)
else:
min_qty = []
break break
if completed: if min_qty:
job_cards = frappe.get_all( qty = min(min_qty)
"Job Card",
filters={"work_order": self.work_order, "docstatus": ("!=", 2)},
fields="sum(transferred_qty) as qty",
group_by="operation_id",
)
if job_cards: doc.db_set("material_transferred_for_manufacturing", qty)
qty = min(d.qty for d in job_cards)
doc.db_set("material_transferred_for_manufacturing", qty)
self.set_status(update_status) self.set_status(update_status)

View File

@@ -47,6 +47,7 @@ frappe.ui.form.on("Project", {
frm.set_query("sales_order", function () { frm.set_query("sales_order", function () {
var filters = { var filters = {
project: ["in", frm.doc.__islocal ? [""] : [frm.doc.name, ""]], project: ["in", frm.doc.__islocal ? [""] : [frm.doc.name, ""]],
company: frm.doc.company,
}; };
if (frm.doc.customer) { if (frm.doc.customer) {

View File

@@ -15,6 +15,7 @@ def execute(filters=None):
filters=filters, filters=filters,
fields=[ fields=[
"name", "name",
"project_name",
"status", "status",
"percent_complete", "percent_complete",
"expected_start_date", "expected_start_date",
@@ -48,6 +49,11 @@ def get_columns():
"options": "Project", "options": "Project",
"width": 200, "width": 200,
}, },
{
"fieldname": "project_name",
"label": _("Project Name"),
"width": 200,
},
{ {
"fieldname": "project_type", "fieldname": "project_type",
"label": _("Type"), "label": _("Type"),
@@ -82,7 +88,7 @@ def get_chart_data(data):
overdue = [] overdue = []
for project in data: for project in data:
labels.append(project.name) labels.append(project.project_name)
total.append(project.total_tasks) total.append(project.total_tasks)
completed.append(project.completed_tasks) completed.append(project.completed_tasks)
overdue.append(project.overdue_tasks) overdue.append(project.overdue_tasks)

View File

@@ -2213,7 +2213,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
payment_terms_template() { payment_terms_template() {
var me = this; var me = this;
const doc = this.frm.doc; const doc = this.frm.doc;
if(doc.payment_terms_template && doc.doctype !== 'Delivery Note') { if(doc.payment_terms_template && doc.doctype !== 'Delivery Note' && doc.is_return == 0) {
var posting_date = doc.posting_date || doc.transaction_date; var posting_date = doc.posting_date || doc.transaction_date;
frappe.call({ frappe.call({
method: "erpnext.controllers.accounts_controller.get_payment_terms", method: "erpnext.controllers.accounts_controller.get_payment_terms",

View File

@@ -335,11 +335,16 @@ def validate_serial_no(sle, item_det):
if sr.work_order and work_order and sr.work_order == work_order: if sr.work_order and work_order and sr.work_order == work_order:
allow_existing_serial_no = True allow_existing_serial_no = True
if not allow_existing_serial_no and sle.voucher_type in [ if (
"Stock Entry", not allow_existing_serial_no
"Purchase Receipt", and sle.voucher_type
"Purchase Invoice", in [
]: "Stock Entry",
"Purchase Receipt",
"Purchase Invoice",
]
and cint(sle.actual_qty) > 0
):
msg = "" msg = ""
if sle.voucher_type == "Stock Entry": if sle.voucher_type == "Stock Entry":

View File

@@ -714,7 +714,17 @@ frappe.ui.form.on('Stock Entry', {
} }
}); });
frappe.ui.form.on('Stock Entry Detail', { frappe.ui.form.on('Stock Entry Detail', {
set_basic_rate_manually(frm, cdt, cdn) {
let row = locals[cdt][cdn];
frm.fields_dict.items.grid.update_docfield_property(
"basic_rate",
"read_only",
row?.set_basic_rate_manually ? 0 : 1
);
},
qty: function(frm, cdt, cdn) { qty: function(frm, cdt, cdn) {
frm.events.set_serial_no(frm, cdt, cdn, () => { frm.events.set_serial_no(frm, cdt, cdn, () => {
frm.events.set_basic_rate(frm, cdt, cdn); frm.events.set_basic_rate(frm, cdt, cdn);

View File

@@ -5268,6 +5268,7 @@ Request for Quotation Supplier,Angebotsanfrage Lieferant,
Send Email,E-Mail absenden, Send Email,E-Mail absenden,
Quote Status,Zitat Status, Quote Status,Zitat Status,
Download PDF,PDF Herunterladen, Download PDF,PDF Herunterladen,
Download PDF for Supplier,PDF für Lieferanten herunterladen,
Supplier of Goods or Services.,Lieferant von Waren oder Dienstleistungen., Supplier of Goods or Services.,Lieferant von Waren oder Dienstleistungen.,
Name and Type,Name und Typ, Name and Type,Name und Typ,
SUP-.YYYY.-,SUP-.YYYY.-, SUP-.YYYY.-,SUP-.YYYY.-,
@@ -6624,6 +6625,7 @@ This is a location where operations are executed.,"Dies ist ein Ort, an dem Oper
This is a location where final product stored.,"Dies ist ein Ort, an dem das Endprodukt gelagert wird.", This is a location where final product stored.,"Dies ist ein Ort, an dem das Endprodukt gelagert wird.",
Scrap Warehouse,Ausschusslager, Scrap Warehouse,Ausschusslager,
This is a location where scraped materials are stored.,"Dies ist ein Ort, an dem abgekratzte Materialien gelagert werden.", This is a location where scraped materials are stored.,"Dies ist ein Ort, an dem abgekratzte Materialien gelagert werden.",
This is a preview of the email to be sent. A PDF of the document will automatically be attached with the email.,Dies ist eine Vorschau der zu sendenden E-Mail. Ein PDF des Dokuments wird automatisch an die E-Mail angehängt.,
Required Items,Erforderliche Elemente, Required Items,Erforderliche Elemente,
Actual Start Date,Tatsächliches Startdatum, Actual Start Date,Tatsächliches Startdatum,
Planned End Date,Geplantes Enddatum, Planned End Date,Geplantes Enddatum,
Can't render this file because it is too large.