mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-19 21:19:19 +00:00
Merge branch 'develop' of https://github.com/frappe/erpnext into #34282-Record-advance-payment-as-a-liability
This commit is contained in:
@@ -616,7 +616,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.events.set_unallocated_amount(frm);
|
frm.events.set_unallocated_amount(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
get_outstanding_invoice: function(frm) {
|
get_outstanding_invoices_or_orders: function(frm, get_outstanding_invoices, get_orders_to_be_billed) {
|
||||||
const today = frappe.datetime.get_today();
|
const today = frappe.datetime.get_today();
|
||||||
const fields = [
|
const fields = [
|
||||||
{fieldtype:"Section Break", label: __("Posting Date")},
|
{fieldtype:"Section Break", label: __("Posting Date")},
|
||||||
@@ -646,12 +646,29 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
|
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let btn_text = "";
|
||||||
|
|
||||||
|
if (get_outstanding_invoices) {
|
||||||
|
btn_text = "Get Outstanding Invoices";
|
||||||
|
}
|
||||||
|
else if (get_orders_to_be_billed) {
|
||||||
|
btn_text = "Get Outstanding Orders";
|
||||||
|
}
|
||||||
|
|
||||||
frappe.prompt(fields, function(filters){
|
frappe.prompt(fields, function(filters){
|
||||||
frappe.flags.allocate_payment_amount = true;
|
frappe.flags.allocate_payment_amount = true;
|
||||||
frm.events.validate_filters_data(frm, filters);
|
frm.events.validate_filters_data(frm, filters);
|
||||||
frm.doc.cost_center = filters.cost_center;
|
frm.doc.cost_center = filters.cost_center;
|
||||||
frm.events.get_outstanding_documents(frm, filters);
|
frm.events.get_outstanding_documents(frm, filters, get_outstanding_invoices, get_orders_to_be_billed);
|
||||||
}, __("Filters"), __("Get Outstanding Documents"));
|
}, __("Filters"), __(btn_text));
|
||||||
|
},
|
||||||
|
|
||||||
|
get_outstanding_invoices: function(frm) {
|
||||||
|
frm.events.get_outstanding_invoices_or_orders(frm, true, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
get_outstanding_orders: function(frm) {
|
||||||
|
frm.events.get_outstanding_invoices_or_orders(frm, false, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
validate_filters_data: function(frm, filters) {
|
validate_filters_data: function(frm, filters) {
|
||||||
@@ -677,7 +694,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
get_outstanding_documents: function(frm, filters) {
|
get_outstanding_documents: function(frm, filters, get_outstanding_invoices, get_orders_to_be_billed) {
|
||||||
frm.clear_table("references");
|
frm.clear_table("references");
|
||||||
|
|
||||||
if(!frm.doc.party) {
|
if(!frm.doc.party) {
|
||||||
@@ -701,6 +718,13 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
args[key] = filters[key];
|
args[key] = filters[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (get_outstanding_invoices) {
|
||||||
|
args["get_outstanding_invoices"] = true;
|
||||||
|
}
|
||||||
|
else if (get_orders_to_be_billed) {
|
||||||
|
args["get_orders_to_be_billed"] = true;
|
||||||
|
}
|
||||||
|
|
||||||
frappe.flags.allocate_payment_amount = filters['allocate_payment_amount'];
|
frappe.flags.allocate_payment_amount = filters['allocate_payment_amount'];
|
||||||
|
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
|
|||||||
@@ -48,7 +48,8 @@
|
|||||||
"base_received_amount",
|
"base_received_amount",
|
||||||
"base_received_amount_after_tax",
|
"base_received_amount_after_tax",
|
||||||
"section_break_14",
|
"section_break_14",
|
||||||
"get_outstanding_invoice",
|
"get_outstanding_invoices",
|
||||||
|
"get_outstanding_orders",
|
||||||
"references",
|
"references",
|
||||||
"section_break_34",
|
"section_break_34",
|
||||||
"total_allocated_amount",
|
"total_allocated_amount",
|
||||||
@@ -355,12 +356,6 @@
|
|||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Reference"
|
"label": "Reference"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"depends_on": "eval:doc.docstatus==0",
|
|
||||||
"fieldname": "get_outstanding_invoice",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"label": "Get Outstanding Invoice"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "references",
|
"fieldname": "references",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
@@ -728,12 +723,24 @@
|
|||||||
"fieldname": "section_break_60",
|
"fieldname": "section_break_60",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hide_border": 1
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.docstatus==0",
|
||||||
|
"fieldname": "get_outstanding_invoices",
|
||||||
|
"fieldtype": "Button",
|
||||||
|
"label": "Get Outstanding Invoices"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.docstatus==0",
|
||||||
|
"fieldname": "get_outstanding_orders",
|
||||||
|
"fieldtype": "Button",
|
||||||
|
"label": "Get Outstanding Orders"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-06-07 14:36:50.521884",
|
"modified": "2023-06-19 11:38:04.387219",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
|||||||
@@ -181,6 +181,19 @@ class PaymentEntry(AccountsController):
|
|||||||
if self.payment_type == "Internal Transfer":
|
if self.payment_type == "Internal Transfer":
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.party_type in ("Customer", "Supplier"):
|
||||||
|
self.validate_allocated_amount_with_latest_data()
|
||||||
|
else:
|
||||||
|
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
|
||||||
|
for d in self.get("references"):
|
||||||
|
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(d.outstanding_amount):
|
||||||
|
frappe.throw(fail_message.format(d.idx))
|
||||||
|
|
||||||
|
# Check for negative outstanding invoices as well
|
||||||
|
if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(d.outstanding_amount):
|
||||||
|
frappe.throw(fail_message.format(d.idx))
|
||||||
|
|
||||||
|
def validate_allocated_amount_with_latest_data(self):
|
||||||
latest_references = get_outstanding_reference_documents(
|
latest_references = get_outstanding_reference_documents(
|
||||||
{
|
{
|
||||||
"posting_date": self.posting_date,
|
"posting_date": self.posting_date,
|
||||||
@@ -189,6 +202,8 @@ class PaymentEntry(AccountsController):
|
|||||||
"payment_type": self.payment_type,
|
"payment_type": self.payment_type,
|
||||||
"party": self.party,
|
"party": self.party,
|
||||||
"party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to,
|
"party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to,
|
||||||
|
"get_outstanding_invoices": True,
|
||||||
|
"get_orders_to_be_billed": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -198,7 +213,7 @@ class PaymentEntry(AccountsController):
|
|||||||
d = frappe._dict(d)
|
d = frappe._dict(d)
|
||||||
latest_lookup.update({(d.voucher_type, d.voucher_no): d})
|
latest_lookup.update({(d.voucher_type, d.voucher_no): d})
|
||||||
|
|
||||||
for d in self.get("references").copy():
|
for d in self.get("references"):
|
||||||
latest = latest_lookup.get((d.reference_doctype, d.reference_name))
|
latest = latest_lookup.get((d.reference_doctype, d.reference_name))
|
||||||
|
|
||||||
# The reference has already been fully paid
|
# The reference has already been fully paid
|
||||||
@@ -213,22 +228,18 @@ class PaymentEntry(AccountsController):
|
|||||||
):
|
):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
"{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' button to get the latest outstanding amount."
|
"{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts."
|
||||||
).format(d.reference_doctype, d.reference_name)
|
).format(d.reference_doctype, d.reference_name)
|
||||||
)
|
)
|
||||||
|
|
||||||
d.outstanding_amount = latest.outstanding_amount
|
|
||||||
|
|
||||||
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
|
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
|
||||||
|
|
||||||
if (flt(d.allocated_amount)) > 0:
|
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount):
|
||||||
if flt(d.allocated_amount) > flt(d.outstanding_amount):
|
frappe.throw(fail_message.format(d.idx))
|
||||||
frappe.throw(fail_message.format(d.idx))
|
|
||||||
|
|
||||||
# Check for negative outstanding invoices as well
|
# Check for negative outstanding invoices as well
|
||||||
if flt(d.allocated_amount) < 0:
|
if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount):
|
||||||
if flt(d.allocated_amount) < flt(d.outstanding_amount):
|
frappe.throw(fail_message.format(d.idx))
|
||||||
frappe.throw(fail_message.format(d.idx))
|
|
||||||
|
|
||||||
def delink_advance_entry_references(self):
|
def delink_advance_entry_references(self):
|
||||||
for reference in self.references:
|
for reference in self.references:
|
||||||
@@ -1432,62 +1443,75 @@ def get_outstanding_reference_documents(args):
|
|||||||
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
||||||
common_filter.append(ple.company == args.get("company"))
|
common_filter.append(ple.company == args.get("company"))
|
||||||
|
|
||||||
outstanding_invoices = get_outstanding_invoices(
|
outstanding_invoices = []
|
||||||
args.get("party_type"),
|
|
||||||
args.get("party"),
|
|
||||||
args.get("party_account"),
|
|
||||||
common_filter=common_filter,
|
|
||||||
posting_date=posting_and_due_date,
|
|
||||||
min_outstanding=args.get("outstanding_amt_greater_than"),
|
|
||||||
max_outstanding=args.get("outstanding_amt_less_than"),
|
|
||||||
accounting_dimensions=accounting_dimensions_filter,
|
|
||||||
)
|
|
||||||
|
|
||||||
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
|
|
||||||
|
|
||||||
for d in outstanding_invoices:
|
|
||||||
d["exchange_rate"] = 1
|
|
||||||
if party_account_currency != company_currency:
|
|
||||||
if d.voucher_type in frappe.get_hooks("invoice_doctypes"):
|
|
||||||
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
|
|
||||||
elif d.voucher_type == "Journal Entry":
|
|
||||||
d["exchange_rate"] = get_exchange_rate(
|
|
||||||
party_account_currency, company_currency, d.posting_date
|
|
||||||
)
|
|
||||||
if d.voucher_type in ("Purchase Invoice"):
|
|
||||||
d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no")
|
|
||||||
|
|
||||||
# Get all SO / PO which are not fully billed or against which full advance not paid
|
|
||||||
orders_to_be_billed = []
|
|
||||||
orders_to_be_billed = get_orders_to_be_billed(
|
|
||||||
args.get("posting_date"),
|
|
||||||
args.get("party_type"),
|
|
||||||
args.get("party"),
|
|
||||||
args.get("company"),
|
|
||||||
party_account_currency,
|
|
||||||
company_currency,
|
|
||||||
filters=args,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get negative outstanding sales /purchase invoices
|
|
||||||
negative_outstanding_invoices = []
|
negative_outstanding_invoices = []
|
||||||
if args.get("party_type") != "Employee" and not args.get("voucher_no"):
|
|
||||||
negative_outstanding_invoices = get_negative_outstanding_invoices(
|
if args.get("get_outstanding_invoices"):
|
||||||
|
outstanding_invoices = get_outstanding_invoices(
|
||||||
args.get("party_type"),
|
args.get("party_type"),
|
||||||
args.get("party"),
|
args.get("party"),
|
||||||
args.get("party_account"),
|
args.get("party_account"),
|
||||||
|
common_filter=common_filter,
|
||||||
|
posting_date=posting_and_due_date,
|
||||||
|
min_outstanding=args.get("outstanding_amt_greater_than"),
|
||||||
|
max_outstanding=args.get("outstanding_amt_less_than"),
|
||||||
|
accounting_dimensions=accounting_dimensions_filter,
|
||||||
|
)
|
||||||
|
|
||||||
|
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
|
||||||
|
|
||||||
|
for d in outstanding_invoices:
|
||||||
|
d["exchange_rate"] = 1
|
||||||
|
if party_account_currency != company_currency:
|
||||||
|
if d.voucher_type in frappe.get_hooks("invoice_doctypes"):
|
||||||
|
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
|
||||||
|
elif d.voucher_type == "Journal Entry":
|
||||||
|
d["exchange_rate"] = get_exchange_rate(
|
||||||
|
party_account_currency, company_currency, d.posting_date
|
||||||
|
)
|
||||||
|
if d.voucher_type in ("Purchase Invoice"):
|
||||||
|
d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no")
|
||||||
|
|
||||||
|
# Get negative outstanding sales /purchase invoices
|
||||||
|
if args.get("party_type") != "Employee" and not args.get("voucher_no"):
|
||||||
|
negative_outstanding_invoices = get_negative_outstanding_invoices(
|
||||||
|
args.get("party_type"),
|
||||||
|
args.get("party"),
|
||||||
|
args.get("party_account"),
|
||||||
|
party_account_currency,
|
||||||
|
company_currency,
|
||||||
|
condition=condition,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get all SO / PO which are not fully billed or against which full advance not paid
|
||||||
|
orders_to_be_billed = []
|
||||||
|
if args.get("get_orders_to_be_billed"):
|
||||||
|
orders_to_be_billed = get_orders_to_be_billed(
|
||||||
|
args.get("posting_date"),
|
||||||
|
args.get("party_type"),
|
||||||
|
args.get("party"),
|
||||||
|
args.get("company"),
|
||||||
party_account_currency,
|
party_account_currency,
|
||||||
company_currency,
|
company_currency,
|
||||||
condition=condition,
|
filters=args,
|
||||||
)
|
)
|
||||||
|
|
||||||
data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
|
data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
|
if args.get("get_outstanding_invoices") and args.get("get_orders_to_be_billed"):
|
||||||
|
ref_document_type = "invoices or orders"
|
||||||
|
elif args.get("get_outstanding_invoices"):
|
||||||
|
ref_document_type = "invoices"
|
||||||
|
elif args.get("get_orders_to_be_billed"):
|
||||||
|
ref_document_type = "orders"
|
||||||
|
|
||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
_(
|
_(
|
||||||
"No outstanding invoices found for the {0} {1} which qualify the filters you have specified."
|
"No outstanding {0} found for the {1} {2} which qualify the filters you have specified."
|
||||||
).format(_(args.get("party_type")).lower(), frappe.bold(args.get("party")))
|
).format(
|
||||||
|
ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party"))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|||||||
@@ -320,6 +320,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval: !doc.is_debit_note",
|
||||||
"fieldname": "is_return",
|
"fieldname": "is_return",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hide_days": 1,
|
"hide_days": 1,
|
||||||
@@ -1960,6 +1961,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval: !doc.is_return",
|
||||||
"description": "Issue a debit note with 0 qty against an existing Sales Invoice",
|
"description": "Issue a debit note with 0 qty against an existing Sales Invoice",
|
||||||
"fieldname": "is_debit_note",
|
"fieldname": "is_debit_note",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
@@ -2155,7 +2157,7 @@
|
|||||||
"link_fieldname": "consolidated_invoice"
|
"link_fieldname": "consolidated_invoice"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2023-06-03 16:22:16.219333",
|
"modified": "2023-06-19 16:02:05.309332",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@@ -125,7 +125,8 @@
|
|||||||
"oldfieldname": "purpose",
|
"oldfieldname": "purpose",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor",
|
"options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor",
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
@@ -678,7 +679,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-06-16 14:59:10.917235",
|
"modified": "2023-06-19 18:23:40.748114",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Entry",
|
"name": "Stock Entry",
|
||||||
|
|||||||
Reference in New Issue
Block a user