feat: landed cost report

This commit is contained in:
Rohit Waghchaure
2025-07-31 16:20:56 +05:30
parent 0707c9d732
commit a0bb8411ef
5 changed files with 250 additions and 16 deletions

View File

@@ -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 (

View 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,
};
},
},
],
};

View File

@@ -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
}

View 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