mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-03 04:09:11 +00:00
Merge pull request #35806 from frappe/version-13-hotfix
chore: release v13
This commit is contained in:
@@ -623,7 +623,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")},
|
||||||
@@ -653,12 +653,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) {
|
||||||
@@ -684,7 +701,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) {
|
||||||
@@ -708,6 +725,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",
|
||||||
@@ -353,12 +354,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",
|
||||||
@@ -726,12 +721,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": "2022-02-23 20:08:39.559814",
|
"modified": "2023-06-19 11:38:04.387219",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
|||||||
@@ -174,6 +174,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 +200,7 @@ 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)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1365,32 +1367,48 @@ def get_outstanding_reference_documents(args):
|
|||||||
if args.get("company"):
|
if args.get("company"):
|
||||||
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
||||||
|
|
||||||
outstanding_invoices = get_outstanding_invoices(
|
outstanding_invoices = []
|
||||||
args.get("party_type"),
|
negative_outstanding_invoices = []
|
||||||
args.get("party"),
|
|
||||||
args.get("party_account"),
|
|
||||||
args.get("company"),
|
|
||||||
filters=args,
|
|
||||||
condition=condition,
|
|
||||||
)
|
|
||||||
|
|
||||||
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
|
if args.get("get_outstanding_invoices"):
|
||||||
|
outstanding_invoices = get_outstanding_invoices(
|
||||||
|
args.get("party_type"),
|
||||||
|
args.get("party"),
|
||||||
|
args.get("party_account"),
|
||||||
|
args.get("company"),
|
||||||
|
filters=args,
|
||||||
|
condition=condition,
|
||||||
|
)
|
||||||
|
|
||||||
for d in outstanding_invoices:
|
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
|
||||||
d["exchange_rate"] = 1
|
|
||||||
if party_account_currency != company_currency:
|
for d in outstanding_invoices:
|
||||||
if d.voucher_type in ("Sales Invoice", "Purchase Invoice", "Expense Claim"):
|
d["exchange_rate"] = 1
|
||||||
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
|
if party_account_currency != company_currency:
|
||||||
elif d.voucher_type == "Journal Entry":
|
if d.voucher_type in ("Sales Invoice", "Purchase Invoice", "Expense Claim"):
|
||||||
d["exchange_rate"] = get_exchange_rate(
|
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
|
||||||
party_account_currency, company_currency, d.posting_date
|
elif d.voucher_type == "Journal Entry":
|
||||||
)
|
d["exchange_rate"] = get_exchange_rate(
|
||||||
if d.voucher_type in ("Purchase Invoice"):
|
party_account_currency, company_currency, d.posting_date
|
||||||
d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no")
|
)
|
||||||
|
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
|
||||||
|
negative_outstanding_invoices = []
|
||||||
|
if args.get("party_type") not in ["Student", "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
|
# Get all SO / PO which are not fully billed or against which full advance not paid
|
||||||
orders_to_be_billed = []
|
orders_to_be_billed = []
|
||||||
if args.get("party_type") != "Student":
|
if args.get("get_orders_to_be_billed") and args.get("party_type") != "Student":
|
||||||
orders_to_be_billed = get_orders_to_be_billed(
|
orders_to_be_billed = get_orders_to_be_billed(
|
||||||
args.get("posting_date"),
|
args.get("posting_date"),
|
||||||
args.get("party_type"),
|
args.get("party_type"),
|
||||||
@@ -1401,25 +1419,22 @@ def get_outstanding_reference_documents(args):
|
|||||||
filters=args,
|
filters=args,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get negative outstanding sales /purchase invoices
|
|
||||||
negative_outstanding_invoices = []
|
|
||||||
if args.get("party_type") not in ["Student", "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,
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -125,12 +125,14 @@ def get_revenue(data, period_list, include_in_gross=1):
|
|||||||
|
|
||||||
data_to_be_removed = True
|
data_to_be_removed = True
|
||||||
while data_to_be_removed:
|
while data_to_be_removed:
|
||||||
revenue, data_to_be_removed = remove_parent_with_no_child(revenue, period_list)
|
revenue, data_to_be_removed = remove_parent_with_no_child(revenue)
|
||||||
revenue = adjust_account(revenue, period_list)
|
|
||||||
|
adjust_account_totals(revenue, period_list)
|
||||||
|
|
||||||
return copy.deepcopy(revenue)
|
return copy.deepcopy(revenue)
|
||||||
|
|
||||||
|
|
||||||
def remove_parent_with_no_child(data, period_list):
|
def remove_parent_with_no_child(data):
|
||||||
data_to_be_removed = False
|
data_to_be_removed = False
|
||||||
for parent in data:
|
for parent in data:
|
||||||
if "is_group" in parent and parent.get("is_group") == 1:
|
if "is_group" in parent and parent.get("is_group") == 1:
|
||||||
@@ -147,16 +149,19 @@ def remove_parent_with_no_child(data, period_list):
|
|||||||
return data, data_to_be_removed
|
return data, data_to_be_removed
|
||||||
|
|
||||||
|
|
||||||
def adjust_account(data, period_list, consolidated=False):
|
def adjust_account_totals(data, period_list):
|
||||||
leaf_nodes = [item for item in data if item["is_group"] == 0]
|
|
||||||
totals = {}
|
totals = {}
|
||||||
for node in leaf_nodes:
|
for d in reversed(data):
|
||||||
set_total(node, node["total"], data, totals)
|
if d.get("is_group"):
|
||||||
for d in data:
|
for period in period_list:
|
||||||
for period in period_list:
|
# reset totals for group accounts as totals set by get_data doesn't consider include_in_gross check
|
||||||
key = period if consolidated else period.key
|
d[period.key] = sum(
|
||||||
d["total"] = totals[d["account"]]
|
item[period.key] for item in data if item.get("parent_account") == d.get("account")
|
||||||
return data
|
)
|
||||||
|
else:
|
||||||
|
set_total(d, d["total"], data, totals)
|
||||||
|
|
||||||
|
d["total"] = totals[d["account"]]
|
||||||
|
|
||||||
|
|
||||||
def set_total(node, value, complete_list, totals):
|
def set_total(node, value, complete_list, totals):
|
||||||
@@ -191,6 +196,9 @@ def get_profit(
|
|||||||
|
|
||||||
if profit_loss[key]:
|
if profit_loss[key]:
|
||||||
has_value = True
|
has_value = True
|
||||||
|
if not profit_loss.get("total"):
|
||||||
|
profit_loss["total"] = 0
|
||||||
|
profit_loss["total"] += profit_loss[key]
|
||||||
|
|
||||||
if has_value:
|
if has_value:
|
||||||
return profit_loss
|
return profit_loss
|
||||||
@@ -229,6 +237,9 @@ def get_net_profit(
|
|||||||
|
|
||||||
if profit_loss[key]:
|
if profit_loss[key]:
|
||||||
has_value = True
|
has_value = True
|
||||||
|
if not profit_loss.get("total"):
|
||||||
|
profit_loss["total"] = 0
|
||||||
|
profit_loss["total"] += profit_loss[key]
|
||||||
|
|
||||||
if has_value:
|
if has_value:
|
||||||
return profit_loss
|
return profit_loss
|
||||||
|
|||||||
@@ -466,15 +466,19 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None,
|
|||||||
"cost_center": depreciation_cost_center,
|
"cost_center": depreciation_cost_center,
|
||||||
"posting_date": date,
|
"posting_date": date,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"account": accumulated_depr_account,
|
|
||||||
"debit_in_account_currency": accumulated_depr_amount,
|
|
||||||
"debit": accumulated_depr_amount,
|
|
||||||
"cost_center": depreciation_cost_center,
|
|
||||||
"posting_date": date,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if accumulated_depr_amount:
|
||||||
|
gl_entries.append(
|
||||||
|
{
|
||||||
|
"account": accumulated_depr_account,
|
||||||
|
"debit_in_account_currency": accumulated_depr_amount,
|
||||||
|
"debit": accumulated_depr_amount,
|
||||||
|
"cost_center": depreciation_cost_center,
|
||||||
|
"posting_date": date,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
profit_amount = flt(selling_amount) - flt(value_after_depreciation)
|
profit_amount = flt(selling_amount) - flt(value_after_depreciation)
|
||||||
if profit_amount:
|
if profit_amount:
|
||||||
get_profit_gl_entries(
|
get_profit_gl_entries(
|
||||||
|
|||||||
@@ -19,56 +19,6 @@ frappe.query_reports["Fixed Asset Register"] = {
|
|||||||
options: "\nIn Location\nDisposed",
|
options: "\nIn Location\nDisposed",
|
||||||
default: 'In Location'
|
default: 'In Location'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname":"filter_based_on",
|
|
||||||
"label": __("Period Based On"),
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"options": ["Fiscal Year", "Date Range"],
|
|
||||||
"default": "Fiscal Year",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname":"from_date",
|
|
||||||
"label": __("Start Date"),
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12),
|
|
||||||
"depends_on": "eval: doc.filter_based_on == 'Date Range'",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname":"to_date",
|
|
||||||
"label": __("End Date"),
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"default": frappe.datetime.nowdate(),
|
|
||||||
"depends_on": "eval: doc.filter_based_on == 'Date Range'",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname":"from_fiscal_year",
|
|
||||||
"label": __("Start Year"),
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"options": "Fiscal Year",
|
|
||||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
|
||||||
"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname":"to_fiscal_year",
|
|
||||||
"label": __("End Year"),
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"options": "Fiscal Year",
|
|
||||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
|
||||||
"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname":"date_based_on",
|
|
||||||
"label": __("Date Based On"),
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"options": ["Purchase Date", "Available For Use Date"],
|
|
||||||
"default": "Purchase Date",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
fieldname:"asset_category",
|
fieldname:"asset_category",
|
||||||
label: __("Asset Category"),
|
label: __("Asset Category"),
|
||||||
@@ -89,22 +39,67 @@ frappe.query_reports["Fixed Asset Register"] = {
|
|||||||
default: "--Select a group--",
|
default: "--Select a group--",
|
||||||
reqd: 1
|
reqd: 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
fieldname:"finance_book",
|
|
||||||
label: __("Finance Book"),
|
|
||||||
fieldtype: "Link",
|
|
||||||
options: "Finance Book",
|
|
||||||
depends_on: "eval: doc.filter_by_finance_book == 1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldname:"filter_by_finance_book",
|
|
||||||
label: __("Filter by Finance Book"),
|
|
||||||
fieldtype: "Check"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
fieldname:"only_existing_assets",
|
fieldname:"only_existing_assets",
|
||||||
label: __("Only existing assets"),
|
label: __("Only existing assets"),
|
||||||
fieldtype: "Check"
|
fieldtype: "Check"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldname:"finance_book",
|
||||||
|
label: __("Finance Book"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Finance Book",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "include_default_book_assets",
|
||||||
|
"label": __("Include Default Book Assets"),
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"default": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"filter_based_on",
|
||||||
|
"label": __("Period Based On"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": ["--Select a period--", "Fiscal Year", "Date Range"],
|
||||||
|
"default": "--Select a period--",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"from_date",
|
||||||
|
"label": __("Start Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12),
|
||||||
|
"depends_on": "eval: doc.filter_based_on == 'Date Range'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"to_date",
|
||||||
|
"label": __("End Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": frappe.datetime.nowdate(),
|
||||||
|
"depends_on": "eval: doc.filter_based_on == 'Date Range'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"from_fiscal_year",
|
||||||
|
"label": __("Start Year"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Fiscal Year",
|
||||||
|
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||||
|
"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"to_fiscal_year",
|
||||||
|
"label": __("End Year"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Fiscal Year",
|
||||||
|
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||||
|
"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"date_based_on",
|
||||||
|
"label": __("Date Based On"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": ["Purchase Date", "Available For Use Date"],
|
||||||
|
"default": "Purchase Date",
|
||||||
|
"depends_on": "eval: doc.filter_based_on == 'Date Range' || doc.filter_based_on == 'Fiscal Year'",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.query_builder.functions import Sum
|
from frappe.query_builder.functions import IfNull, Sum
|
||||||
from frappe.utils import cstr, flt, formatdate, getdate
|
from frappe.utils import cstr, flt, formatdate, getdate
|
||||||
|
|
||||||
from erpnext.accounts.report.financial_statements import (
|
from erpnext.accounts.report.financial_statements import (
|
||||||
@@ -13,7 +15,6 @@ from erpnext.accounts.report.financial_statements import (
|
|||||||
validate_fiscal_year,
|
validate_fiscal_year,
|
||||||
)
|
)
|
||||||
from erpnext.assets.doctype.asset.asset import get_asset_value_after_depreciation
|
from erpnext.assets.doctype.asset.asset import get_asset_value_after_depreciation
|
||||||
from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts
|
|
||||||
|
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
@@ -64,11 +65,9 @@ def get_conditions(filters):
|
|||||||
|
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
conditions = get_conditions(filters)
|
conditions = get_conditions(filters)
|
||||||
depreciation_amount_map = get_finance_book_value_map(filters)
|
|
||||||
pr_supplier_map = get_purchase_receipt_supplier_map()
|
pr_supplier_map = get_purchase_receipt_supplier_map()
|
||||||
pi_supplier_map = get_purchase_invoice_supplier_map()
|
pi_supplier_map = get_purchase_invoice_supplier_map()
|
||||||
|
|
||||||
@@ -102,20 +101,27 @@ def get_data(filters):
|
|||||||
]
|
]
|
||||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
|
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
|
||||||
|
|
||||||
assets_linked_to_fb = None
|
assets_linked_to_fb = get_assets_linked_to_fb(filters)
|
||||||
|
|
||||||
if filters.filter_by_finance_book:
|
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
|
||||||
assets_linked_to_fb = frappe.db.get_all(
|
|
||||||
doctype="Asset Finance Book",
|
if filters.include_default_book_assets and company_fb:
|
||||||
filters={"finance_book": filters.finance_book or ("is", "not set")},
|
finance_book = company_fb
|
||||||
pluck="parent",
|
elif filters.finance_book:
|
||||||
)
|
finance_book = filters.finance_book
|
||||||
|
else:
|
||||||
|
finance_book = None
|
||||||
|
|
||||||
|
depreciation_amount_map = get_asset_depreciation_amount_map(filters, finance_book)
|
||||||
|
|
||||||
for asset in assets_record:
|
for asset in assets_record:
|
||||||
if assets_linked_to_fb and asset.asset_id not in assets_linked_to_fb:
|
if assets_linked_to_fb and asset.asset_id not in assets_linked_to_fb:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
asset_value = get_asset_value_after_depreciation(asset.asset_id, filters.finance_book)
|
asset_value = get_asset_value_after_depreciation(
|
||||||
|
asset.asset_id, finance_book
|
||||||
|
) or get_asset_value_after_depreciation(asset.asset_id)
|
||||||
|
|
||||||
row = {
|
row = {
|
||||||
"asset_id": asset.asset_id,
|
"asset_id": asset.asset_id,
|
||||||
"asset_name": asset.asset_name,
|
"asset_name": asset.asset_name,
|
||||||
@@ -126,7 +132,7 @@ def get_data(filters):
|
|||||||
or pi_supplier_map.get(asset.purchase_invoice),
|
or pi_supplier_map.get(asset.purchase_invoice),
|
||||||
"gross_purchase_amount": asset.gross_purchase_amount,
|
"gross_purchase_amount": asset.gross_purchase_amount,
|
||||||
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
||||||
"depreciated_amount": get_depreciation_amount_of_asset(asset, depreciation_amount_map, filters),
|
"depreciated_amount": get_depreciation_amount_of_asset(asset, depreciation_amount_map),
|
||||||
"available_for_use_date": asset.available_for_use_date,
|
"available_for_use_date": asset.available_for_use_date,
|
||||||
"location": asset.location,
|
"location": asset.location,
|
||||||
"asset_category": asset.asset_category,
|
"asset_category": asset.asset_category,
|
||||||
@@ -140,14 +146,23 @@ def get_data(filters):
|
|||||||
|
|
||||||
def prepare_chart_data(data, filters):
|
def prepare_chart_data(data, filters):
|
||||||
labels_values_map = {}
|
labels_values_map = {}
|
||||||
date_field = frappe.scrub(filters.date_based_on)
|
if filters.filter_based_on not in ("Date Range", "Fiscal Year"):
|
||||||
|
filters_filter_based_on = "Date Range"
|
||||||
|
date_field = "purchase_date"
|
||||||
|
filters_from_date = min(data, key=lambda a: a.get(date_field)).get(date_field)
|
||||||
|
filters_to_date = max(data, key=lambda a: a.get(date_field)).get(date_field)
|
||||||
|
else:
|
||||||
|
filters_filter_based_on = filters.filter_based_on
|
||||||
|
date_field = frappe.scrub(filters.date_based_on)
|
||||||
|
filters_from_date = filters.from_date
|
||||||
|
filters_to_date = filters.to_date
|
||||||
|
|
||||||
period_list = get_period_list(
|
period_list = get_period_list(
|
||||||
filters.from_fiscal_year,
|
filters.from_fiscal_year,
|
||||||
filters.to_fiscal_year,
|
filters.to_fiscal_year,
|
||||||
filters.from_date,
|
filters_from_date,
|
||||||
filters.to_date,
|
filters_to_date,
|
||||||
filters.filter_based_on,
|
filters_filter_based_on,
|
||||||
"Monthly",
|
"Monthly",
|
||||||
company=filters.company,
|
company=filters.company,
|
||||||
ignore_fiscal_year=True,
|
ignore_fiscal_year=True,
|
||||||
@@ -184,57 +199,76 @@ def prepare_chart_data(data, filters):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_depreciation_amount_of_asset(asset, depreciation_amount_map, filters):
|
def get_assets_linked_to_fb(filters):
|
||||||
if asset.calculate_depreciation:
|
afb = frappe.qb.DocType("Asset Finance Book")
|
||||||
depr_amount = depreciation_amount_map.get(asset.asset_id) or 0.0
|
|
||||||
else:
|
|
||||||
depr_amount = get_manual_depreciation_amount_of_asset(asset, filters)
|
|
||||||
|
|
||||||
return flt(depr_amount, 2)
|
query = frappe.qb.from_(afb).select(
|
||||||
|
afb.parent,
|
||||||
|
|
||||||
def get_finance_book_value_map(filters):
|
|
||||||
date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
|
|
||||||
|
|
||||||
return frappe._dict(
|
|
||||||
frappe.db.sql(
|
|
||||||
""" Select
|
|
||||||
parent, SUM(depreciation_amount)
|
|
||||||
FROM `tabDepreciation Schedule`
|
|
||||||
WHERE
|
|
||||||
parentfield='schedules'
|
|
||||||
AND schedule_date<=%s
|
|
||||||
AND journal_entry IS NOT NULL
|
|
||||||
AND ifnull(finance_book, '')=%s
|
|
||||||
GROUP BY parent""",
|
|
||||||
(date, cstr(filters.finance_book or "")),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if filters.include_default_book_assets:
|
||||||
|
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
|
||||||
|
|
||||||
def get_manual_depreciation_amount_of_asset(asset, filters):
|
if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb):
|
||||||
|
frappe.throw(_("To use a different finance book, please uncheck 'Include Default Book Assets'"))
|
||||||
|
|
||||||
|
query = query.where(
|
||||||
|
(afb.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""]))
|
||||||
|
| (afb.finance_book.isnull())
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
query = query.where(
|
||||||
|
(afb.finance_book.isin([cstr(filters.finance_book), ""])) | (afb.finance_book.isnull())
|
||||||
|
)
|
||||||
|
|
||||||
|
assets_linked_to_fb = list(chain(*query.run(as_list=1)))
|
||||||
|
|
||||||
|
return assets_linked_to_fb
|
||||||
|
|
||||||
|
|
||||||
|
def get_depreciation_amount_of_asset(asset, depreciation_amount_map):
|
||||||
|
return depreciation_amount_map.get(asset.asset_id) or 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def get_asset_depreciation_amount_map(filters, finance_book):
|
||||||
date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
|
date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
|
||||||
|
|
||||||
(_, _, depreciation_expense_account) = get_depreciation_accounts(asset)
|
asset = frappe.qb.DocType("Asset")
|
||||||
|
|
||||||
gle = frappe.qb.DocType("GL Entry")
|
gle = frappe.qb.DocType("GL Entry")
|
||||||
|
aca = frappe.qb.DocType("Asset Category Account")
|
||||||
|
company = frappe.qb.DocType("Company")
|
||||||
|
|
||||||
result = (
|
query = (
|
||||||
frappe.qb.from_(gle)
|
frappe.qb.from_(gle)
|
||||||
.select(Sum(gle.debit))
|
.join(asset)
|
||||||
.where(gle.against_voucher == asset.asset_id)
|
.on(gle.against_voucher == asset.name)
|
||||||
.where(gle.account == depreciation_expense_account)
|
.join(aca)
|
||||||
|
.on((aca.parent == asset.asset_category) & (aca.company_name == asset.company))
|
||||||
|
.join(company)
|
||||||
|
.on(company.name == asset.company)
|
||||||
|
.select(asset.name.as_("asset"), Sum(gle.debit).as_("depreciation_amount"))
|
||||||
|
.where(
|
||||||
|
gle.account == IfNull(aca.depreciation_expense_account, company.depreciation_expense_account)
|
||||||
|
)
|
||||||
.where(gle.debit != 0)
|
.where(gle.debit != 0)
|
||||||
.where(gle.is_cancelled == 0)
|
.where(gle.is_cancelled == 0)
|
||||||
.where(gle.posting_date <= date)
|
.where(asset.docstatus == 1)
|
||||||
).run()
|
.groupby(asset.name)
|
||||||
|
)
|
||||||
|
|
||||||
if result and result[0] and result[0][0]:
|
if finance_book:
|
||||||
depr_amount = result[0][0]
|
query = query.where(
|
||||||
|
(gle.finance_book.isin([cstr(finance_book), ""])) | (gle.finance_book.isnull())
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
depr_amount = 0
|
query = query.where((gle.finance_book.isin([""])) | (gle.finance_book.isnull()))
|
||||||
|
|
||||||
return depr_amount
|
if filters.filter_based_on in ("Date Range", "Fiscal Year"):
|
||||||
|
query = query.where(gle.posting_date <= date)
|
||||||
|
|
||||||
|
asset_depr_amount_map = query.run()
|
||||||
|
|
||||||
|
return dict(asset_depr_amount_map)
|
||||||
|
|
||||||
|
|
||||||
def get_purchase_receipt_supplier_map():
|
def get_purchase_receipt_supplier_map():
|
||||||
|
|||||||
@@ -293,8 +293,8 @@ def get_last_accrual_date(loan, posting_date):
|
|||||||
# interest for last interest accrual date is already booked, so add 1 day
|
# interest for last interest accrual date is already booked, so add 1 day
|
||||||
last_disbursement_date = get_last_disbursement_date(loan, posting_date)
|
last_disbursement_date = get_last_disbursement_date(loan, posting_date)
|
||||||
|
|
||||||
if last_disbursement_date and getdate(last_disbursement_date) > getdate(
|
if last_disbursement_date and getdate(last_disbursement_date) > add_days(
|
||||||
last_interest_accrual_date
|
getdate(last_interest_accrual_date), 1
|
||||||
):
|
):
|
||||||
last_interest_accrual_date = last_disbursement_date
|
last_interest_accrual_date = last_disbursement_date
|
||||||
|
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
self.po_required()
|
self.po_required()
|
||||||
|
self.validate_items_quality_inspection()
|
||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
self.validate_uom_is_integer("uom", ["qty", "received_qty"])
|
self.validate_uom_is_integer("uom", ["qty", "received_qty"])
|
||||||
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
||||||
@@ -183,6 +184,26 @@ class PurchaseReceipt(BuyingController):
|
|||||||
if not d.purchase_order:
|
if not d.purchase_order:
|
||||||
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
||||||
|
|
||||||
|
def validate_items_quality_inspection(self):
|
||||||
|
for item in self.get("items"):
|
||||||
|
if item.quality_inspection:
|
||||||
|
qi = frappe.db.get_value(
|
||||||
|
"Quality Inspection",
|
||||||
|
item.quality_inspection,
|
||||||
|
["reference_type", "reference_name", "item_code"],
|
||||||
|
as_dict=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if qi.reference_type != self.doctype or qi.reference_name != self.name:
|
||||||
|
msg = f"""Row #{item.idx}: Please select a valid Quality Inspection with Reference Type
|
||||||
|
{frappe.bold(self.doctype)} and Reference Name {frappe.bold(self.name)}."""
|
||||||
|
frappe.throw(_(msg))
|
||||||
|
|
||||||
|
if qi.item_code != item.item_code:
|
||||||
|
msg = f"""Row #{item.idx}: Please select a valid Quality Inspection with Item Code
|
||||||
|
{frappe.bold(item.item_code)}."""
|
||||||
|
frappe.throw(_(msg))
|
||||||
|
|
||||||
def get_already_received_qty(self, po, po_detail):
|
def get_already_received_qty(self, po, po_detail):
|
||||||
qty = frappe.db.sql(
|
qty = frappe.db.sql(
|
||||||
"""select sum(qty) from `tabPurchase Receipt Item`
|
"""select sum(qty) from `tabPurchase Receipt Item`
|
||||||
|
|||||||
@@ -122,7 +122,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",
|
||||||
@@ -619,7 +620,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-05-02 05:21:39.060501",
|
"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