mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-15 19:19:17 +00:00
Merge pull request #43892 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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"]:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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.
|
Reference in New Issue
Block a user