mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-17 16:45:02 +00:00
feat: landed cost report
This commit is contained in:
@@ -82,28 +82,12 @@ class LandedCostVoucher(Document):
|
||||
|
||||
self.set_applicable_charges_on_item()
|
||||
self.set_total_vendor_invoices_cost()
|
||||
self.validate_vendor_invoices_cost_with_landed_cost()
|
||||
|
||||
def set_total_vendor_invoices_cost(self):
|
||||
self.total_vendor_invoices_cost = 0.0
|
||||
for row in self.vendor_invoices:
|
||||
self.total_vendor_invoices_cost += flt(row.amount)
|
||||
|
||||
def validate_vendor_invoices_cost_with_landed_cost(self):
|
||||
if not self.total_vendor_invoices_cost:
|
||||
return
|
||||
|
||||
precision = frappe.get_precision("Landed Cost Voucher", "total_vendor_invoices_cost")
|
||||
|
||||
if flt(self.total_vendor_invoices_cost, precision) != flt(self.total_taxes_and_charges, precision):
|
||||
frappe.throw(
|
||||
_("Total Vendor Invoices Cost ({0}) must be equal to the Total Landed Cost ({1}).").format(
|
||||
bold(self.total_vendor_invoices_cost),
|
||||
bold(self.total_taxes_and_charges),
|
||||
),
|
||||
title=_("Incorrect Landed Cost"),
|
||||
)
|
||||
|
||||
def validate_line_items(self):
|
||||
for d in self.get("items"):
|
||||
if (
|
||||
|
||||
0
erpnext/stock/report/landed_cost_report/__init__.py
Normal file
0
erpnext/stock/report/landed_cost_report/__init__.py
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Landed Cost Report"] = {
|
||||
filters: [
|
||||
{
|
||||
fieldname: "company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_user_default("Company"),
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "from_date",
|
||||
label: __("From Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "to_date",
|
||||
label: __("To Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.get_today(),
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "raw_material_voucher_type",
|
||||
label: __("Raw Material Voucher Type"),
|
||||
fieldtype: "Select",
|
||||
options: "\nPurchase Receipt\nPurchase Invoice\nStock Entry\nSubcontracting Receipt",
|
||||
},
|
||||
{
|
||||
fieldname: "raw_material_voucher_no",
|
||||
label: __("Raw Material Voucher No"),
|
||||
fieldtype: "Dynamic Link",
|
||||
get_options: function () {
|
||||
let voucher_type = frappe.query_report.get_filter_value("raw_material_voucher_type");
|
||||
return voucher_type;
|
||||
},
|
||||
get_query: function () {
|
||||
let company = frappe.query_report.get_filter_value("company");
|
||||
let voucher_type = frappe.query_report.get_filter_value("raw_material_voucher_type");
|
||||
let query_filters = {
|
||||
docstatus: 1,
|
||||
company: company,
|
||||
};
|
||||
|
||||
if (voucher_type === "Purchase Invoice") {
|
||||
query_filters["update_stock"] = 1;
|
||||
}
|
||||
|
||||
return {
|
||||
filters: query_filters,
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"add_translate_data": 0,
|
||||
"columns": [],
|
||||
"creation": "2025-07-31 14:36:55.047172",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"json": "{}",
|
||||
"letterhead": null,
|
||||
"modified": "2025-07-31 14:37:37.141783",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Landed Cost Report",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Landed Cost Voucher",
|
||||
"report_name": "Landed Cost Report",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Stock Manager"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Purchase Manager"
|
||||
},
|
||||
{
|
||||
"role": "Purchase User"
|
||||
}
|
||||
],
|
||||
"timeout": 0
|
||||
}
|
||||
152
erpnext/stock/report/landed_cost_report/landed_cost_report.py
Normal file
152
erpnext/stock/report/landed_cost_report/landed_cost_report.py
Normal file
@@ -0,0 +1,152 @@
|
||||
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
def execute(filters: dict | None = None):
|
||||
columns = get_columns()
|
||||
data = get_data(filters)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns() -> list[dict]:
|
||||
return [
|
||||
{
|
||||
"label": _("Landed Cost Id"),
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Link",
|
||||
"options": "Landed Cost Voucher",
|
||||
},
|
||||
{
|
||||
"label": _("Total Landed Cost"),
|
||||
"fieldname": "landed_cost",
|
||||
"fieldtype": "Currency",
|
||||
},
|
||||
{
|
||||
"label": _("Purchase Voucher Type"),
|
||||
"fieldname": "voucher_type",
|
||||
"fieldtype": "Data",
|
||||
"width": 200,
|
||||
},
|
||||
{
|
||||
"label": _("Purchase Voucher No"),
|
||||
"fieldname": "voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"options": "voucher_type",
|
||||
"width": 220,
|
||||
},
|
||||
{
|
||||
"label": _("Vendor Invoice"),
|
||||
"fieldname": "vendor_invoice",
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Invoice",
|
||||
"width": 200,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def get_data(filters) -> list[list]:
|
||||
landed_cost_vouchers = get_landed_cost_vouchers(filters) or {}
|
||||
landed_vouchers = list(landed_cost_vouchers.keys())
|
||||
vendor_invoices = {}
|
||||
if landed_vouchers:
|
||||
vendor_invoices = get_vendor_invoices(landed_vouchers)
|
||||
|
||||
data = []
|
||||
|
||||
print(vendor_invoices)
|
||||
for name, vouchers in landed_cost_vouchers.items():
|
||||
res = {
|
||||
"name": name,
|
||||
}
|
||||
|
||||
last_index = 0
|
||||
vendor_invoice_list = vendor_invoices.get(name, [])
|
||||
for i, d in enumerate(vouchers):
|
||||
if i == 0:
|
||||
res.update(
|
||||
{
|
||||
"landed_cost": d.landed_cost,
|
||||
"voucher_type": d.voucher_type,
|
||||
"voucher_no": d.voucher_no,
|
||||
}
|
||||
)
|
||||
else:
|
||||
res = {
|
||||
"voucher_type": d.voucher_type,
|
||||
"voucher_no": d.voucher_no,
|
||||
}
|
||||
|
||||
if len(vendor_invoice_list) > i:
|
||||
res["vendor_invoice"] = vendor_invoice_list[i]
|
||||
|
||||
data.append(res)
|
||||
last_index = i
|
||||
|
||||
if vendor_invoice_list and len(vendor_invoice_list) > len(vouchers):
|
||||
for row in vendor_invoice_list[last_index + 1 :]:
|
||||
print(row)
|
||||
data.append({"vendor_invoice": row})
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_landed_cost_vouchers(filters):
|
||||
lcv = frappe.qb.DocType("Landed Cost Voucher")
|
||||
lcv_voucher = frappe.qb.DocType("Landed Cost Purchase Receipt")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(lcv)
|
||||
.inner_join(lcv_voucher)
|
||||
.on(lcv.name == lcv_voucher.parent)
|
||||
.select(
|
||||
lcv.name,
|
||||
lcv.total_taxes_and_charges.as_("landed_cost"),
|
||||
lcv_voucher.receipt_document_type.as_("voucher_type"),
|
||||
lcv_voucher.receipt_document.as_("voucher_no"),
|
||||
)
|
||||
.where((lcv.docstatus == 1) & (lcv.company == filters.company))
|
||||
)
|
||||
|
||||
if filters.from_date and filters.to_date:
|
||||
query = query.where(lcv.posting_date.between(filters.from_date, filters.to_date))
|
||||
|
||||
if filters.raw_material_voucher_type:
|
||||
query = query.where(lcv_voucher.receipt_document_type == filters.raw_material_voucher_type)
|
||||
|
||||
if filters.raw_material_voucher_no:
|
||||
query = query.where(lcv_voucher.receipt_document == filters.raw_material_voucher_no)
|
||||
|
||||
data = query.run(as_dict=True) or []
|
||||
result = {}
|
||||
for row in data:
|
||||
result.setdefault((row.name), []).append(row)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_vendor_invoices(landed_vouchers):
|
||||
doctype = frappe.qb.DocType("Landed Cost Vendor Invoice")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(doctype)
|
||||
.select(
|
||||
doctype.parent,
|
||||
doctype.vendor_invoice,
|
||||
)
|
||||
.where((doctype.docstatus == 1) & (doctype.parent.isin(landed_vouchers)))
|
||||
.orderby(
|
||||
doctype.idx,
|
||||
)
|
||||
)
|
||||
|
||||
data = query.run(as_dict=True) or []
|
||||
|
||||
result = {}
|
||||
for row in data:
|
||||
result.setdefault(row.parent, []).append(row.vendor_invoice)
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user