mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-16 03:29:16 +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_applicable_charges_on_item()
|
||||||
self.set_total_vendor_invoices_cost()
|
self.set_total_vendor_invoices_cost()
|
||||||
self.validate_vendor_invoices_cost_with_landed_cost()
|
|
||||||
|
|
||||||
def set_total_vendor_invoices_cost(self):
|
def set_total_vendor_invoices_cost(self):
|
||||||
self.total_vendor_invoices_cost = 0.0
|
self.total_vendor_invoices_cost = 0.0
|
||||||
for row in self.vendor_invoices:
|
for row in self.vendor_invoices:
|
||||||
self.total_vendor_invoices_cost += flt(row.amount)
|
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):
|
def validate_line_items(self):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if (
|
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