Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,4 @@ build/
landms/landms/cleanup.py
DOCUMENTATION.md
TCB_INTEGRATION.md
docs/
5 changes: 5 additions & 0 deletions landms/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

required_apps = ["erpnext"]

doctype_js = {
"Purchase Order": "public/js/purchase_order.js",
"Purchase Invoice": "public/js/purchase_invoice.js",
}

after_install = "landms.install.after_install"

after_migrate = ["landms.utils.import_setup_data"]
Expand Down
4 changes: 2 additions & 2 deletions landms/landms/doctype/land_acquisition/land_acquisition.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ frappe.ui.form.on('Land Acquisition', {
refresh_plot_counts(frm);

frm.add_custom_button('Purchase Order', () => {
frappe.route_options = { land_acquisition: frm.doc.name };
frappe.flags.new_po_land_acquisition = frm.doc.name;
frappe.new_doc('Purchase Order');
}, __('Create'));

frm.add_custom_button('Purchase Invoice', () => {
frappe.route_options = { land_acquisition: frm.doc.name };
frappe.flags.new_pi_land_acquisition = frm.doc.name;
frappe.new_doc('Purchase Invoice');
}, __('Create'));
}
Expand Down
132 changes: 105 additions & 27 deletions landms/landms/doctype/landms_settings/landms_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"is_submittable": 0,
"track_changes": 1,
"fields": [
{
"fieldname": "tab_general",
"fieldtype": "Tab Break",
"label": "General"
},
{
"fieldname": "company_section",
"fieldtype": "Section Break",
Expand All @@ -26,9 +31,58 @@
"reqd": 1
},
{
"fieldname": "accounts_section",
"fieldname": "col_break_company",
"fieldtype": "Column Break"
},
{
"fieldname": "plot_inventory_warehouse",
"fieldtype": "Link",
"label": "Plot Inventory Warehouse",
"options": "Warehouse",
"reqd": 1
},
{
"fieldname": "plot_items_section",
"fieldtype": "Section Break",
"label": "GL Account Mappings"
"label": "Plot Stock Items"
},
{
"fieldname": "residential_plot_item",
"fieldtype": "Link",
"label": "Residential Plot Item",
"options": "Item",
"reqd": 1,
"description": "Stock item used when a Residential plot enters inventory"
},
{
"fieldname": "commercial_plot_item",
"fieldtype": "Link",
"label": "Commercial Plot Item",
"options": "Item",
"reqd": 1,
"description": "Stock item used when a Commercial plot enters inventory"
},
{
"fieldname": "col_break_plot_items",
"fieldtype": "Column Break"
},
{
"fieldname": "mixed_use_plot_item",
"fieldtype": "Link",
"label": "Mixed Use Plot Item",
"options": "Item",
"reqd": 1,
"description": "Stock item used when a Mixed Use plot enters inventory"
},
{
"fieldname": "tab_accounts",
"fieldtype": "Tab Break",
"label": "Accounts"
},
{
"fieldname": "asset_accounts_section",
"fieldtype": "Section Break",
"label": "Asset Accounts"
},
{
"fieldname": "land_under_development_account",
Expand All @@ -45,12 +99,16 @@
"reqd": 1
},
{
"fieldname": "customer_advance_account",
"fieldname": "tcb_bank_account",
"fieldtype": "Link",
"label": "Customer Advances Account",
"label": "TCB Bank Account",
"options": "Account",
"reqd": 1
},
{
"fieldname": "col_break_accounts",
"fieldtype": "Column Break"
},
{
"fieldname": "revenue_account",
"fieldtype": "Link",
Expand All @@ -66,19 +124,36 @@
"reqd": 1
},
{
"fieldname": "tcb_bank_account",
"fieldname": "customer_advance_account",
"fieldtype": "Link",
"label": "TCB Bank Account",
"label": "Customer Advances Account",
"options": "Account",
"reqd": 1
},
{
"fieldname": "liability_accounts_section",
"fieldtype": "Section Break",
"label": "Liability and Income Accounts"
},
{
"fieldname": "government_payable_account",
"fieldtype": "Link",
"label": "Government Payable Account",
"options": "Account",
"reqd": 1
},
{
"fieldname": "seller_payable_account",
"fieldtype": "Link",
"label": "Seller Payable Account",
"options": "Account",
"reqd": 1,
"description": "Creditors account used when land is acquired — Dr Land Under Development / Cr this account"
},
{
"fieldname": "col_break_liabilities",
"fieldtype": "Column Break"
},
{
"fieldname": "forfeited_deposits_account",
"fieldtype": "Link",
Expand All @@ -87,17 +162,14 @@
"reqd": 1
},
{
"fieldname": "seller_payable_account",
"fieldtype": "Link",
"label": "Seller Payable Account",
"options": "Account",
"reqd": 1,
"description": "Creditors account used when land is acquired — Dr Land Under Development / Cr this account"
"fieldname": "tab_application_fee",
"fieldtype": "Tab Break",
"label": "Application Fee"
},
{
"fieldname": "application_fee_section",
"fieldname": "app_fee_details_section",
"fieldtype": "Section Break",
"label": "Application Fee"
"label": "Fee Details"
},
{
"fieldname": "application_fee_item",
Expand All @@ -114,6 +186,10 @@
"reqd": 1,
"description": "Fixed application fee charged per plot before a Sales Order can be created"
},
{
"fieldname": "col_break_app_fee",
"fieldtype": "Column Break"
},
{
"fieldname": "unpaid_application_expiry_days",
"fieldtype": "Int",
Expand All @@ -131,8 +207,9 @@
"description": "Days the paid application reserves the plot before the Sales Order must be created"
},
{
"fieldname": "application_fee_col_break",
"fieldtype": "Column Break"
"fieldname": "app_fee_accounts_section",
"fieldtype": "Section Break",
"label": "Fee Accounts"
},
{
"fieldname": "application_fee_income_account",
Expand All @@ -142,6 +219,10 @@
"reqd": 1,
"description": "Income account where application fees are posted"
},
{
"fieldname": "col_break_app_fee_accounts",
"fieldtype": "Column Break"
},
{
"fieldname": "application_fee_receiving_account",
"fieldtype": "Link",
Expand All @@ -150,21 +231,14 @@
"description": "Default Bank/Cash account where application fee payments are received"
},
{
"fieldname": "warehouse_section",
"fieldtype": "Section Break",
"label": "Warehouse"
},
{
"fieldname": "plot_inventory_warehouse",
"fieldtype": "Link",
"label": "Plot Inventory Warehouse",
"options": "Warehouse",
"reqd": 1
"fieldname": "tab_naming",
"fieldtype": "Tab Break",
"label": "Naming Series"
},
{
"fieldname": "naming_section",
"fieldtype": "Section Break",
"label": "Naming Series"
"label": "Document Naming"
},
{
"fieldname": "naming_series_contract",
Expand All @@ -178,6 +252,10 @@
"label": "Installment Schedule Naming Series",
"default": "ISCH-.YYYY.-.####"
},
{
"fieldname": "col_break_naming",
"fieldtype": "Column Break"
},
{
"fieldname": "naming_series_tcb_log",
"fieldtype": "Data",
Expand Down
6 changes: 2 additions & 4 deletions landms/landms/doctype/plot_handover/plot_handover.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from frappe.utils import get_fullname, today

from landms.landms.doctype.land_acquisition.land_acquisition import sync_land_acquisition_plot_summary
from landms.landms.doctype.plot_master.plot_master import PLOT_TYPE_TO_ITEM
from landms.landms.doctype.plot_master.plot_master import get_plot_item_code


class PlotHandover(Document):
Expand Down Expand Up @@ -126,9 +126,7 @@ def _make_delivery_note_from_sales_order(self, sales_order_name, plot):
return dn

def _make_delivery_note_direct(self, contract, plot, settings):
item_code = PLOT_TYPE_TO_ITEM.get(plot.plot_type)
if not item_code:
frappe.throw(f"No item is mapped for plot type '{plot.plot_type}'.")
item_code = get_plot_item_code(plot.plot_type)

return frappe.get_doc(
{
Expand Down
29 changes: 21 additions & 8 deletions landms/landms/doctype/plot_master/plot_master.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
sync_land_acquisition_plot_summary,
)

# Plot Type → stock Item code (must exist as fixtures with Maintain Stock = Yes)
PLOT_TYPE_TO_ITEM = {
"Residential": "RESIDENTIAL PLOT",
"Commercial": "COMMERCIAL PLOT",
"Mixed Use": "MIXED USE PLOT",
PLOT_TYPE_TO_SETTINGS_FIELD = {
"Residential": "residential_plot_item",
"Commercial": "commercial_plot_item",
"Mixed Use": "mixed_use_plot_item",
}

# Plot Type → Land Acquisition selling-rate field
Expand Down Expand Up @@ -184,9 +183,7 @@ def create_stock_entry(self):
warehouse, against a freshly minted Serial No tied to this plot."""
settings = frappe.get_single("LandMS Settings")

item_code = PLOT_TYPE_TO_ITEM.get(self.plot_type)
if not item_code:
frappe.throw(f"No stock item is mapped for plot type '{self.plot_type}'.")
item_code = get_plot_item_code(self.plot_type, settings)

warehouse = settings.plot_inventory_warehouse
if not warehouse:
Expand Down Expand Up @@ -245,6 +242,22 @@ def cancel_stock_entry(self):
self.db_set("serial_no", None)


def get_plot_item_code(plot_type, settings=None):
"""Return the stock item code for the given plot type from LandMS Settings."""
field = PLOT_TYPE_TO_SETTINGS_FIELD.get(plot_type)
if not field:
frappe.throw(f"Unknown plot type '{plot_type}'.")
if settings is None:
settings = frappe.get_single("LandMS Settings")
item_code = settings.get(field)
if not item_code:
frappe.throw(
f"No stock item is set for plot type '{plot_type}'. "
f"Go to LandMS Settings → Plot Stock Items and set it."
)
return item_code


def get_plot_type_selling_rate(la_values, plot_type):
"""Look up the per-sqm selling rate for the given plot type on the LA."""
rate_field = PLOT_TYPE_TO_RATE_FIELD.get(plot_type)
Expand Down
23 changes: 23 additions & 0 deletions landms/public/js/purchase_invoice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
frappe.ui.form.on('Purchase Invoice', {
onload(frm) {
if (!frm.doc.__islocal) return;
const la = frappe.flags.new_pi_land_acquisition;
if (!la) return;
delete frappe.flags.new_pi_land_acquisition;
frm._land_acquisition = la;
(frm.doc.items || []).forEach(item => {
frappe.model.set_value(item.doctype, item.name, 'land_acquisition', la);
});
}
});

frappe.ui.form.on('Purchase Invoice Item', {
item_code(frm, cdt, cdn) {
if (frm._land_acquisition) {
const row = frappe.get_doc(cdt, cdn);
if (!row.land_acquisition) {
frappe.model.set_value(cdt, cdn, 'land_acquisition', frm._land_acquisition);
}
}
}
});
23 changes: 23 additions & 0 deletions landms/public/js/purchase_order.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
frappe.ui.form.on('Purchase Order', {
onload(frm) {
if (!frm.doc.__islocal) return;
const la = frappe.flags.new_po_land_acquisition;
if (!la) return;
delete frappe.flags.new_po_land_acquisition;
frm._land_acquisition = la;
(frm.doc.items || []).forEach(item => {
frappe.model.set_value(item.doctype, item.name, 'land_acquisition', la);
});
}
});

frappe.ui.form.on('Purchase Order Item', {
item_code(frm, cdt, cdn) {
if (frm._land_acquisition) {
const row = frappe.get_doc(cdt, cdn);
if (!row.land_acquisition) {
frappe.model.set_value(cdt, cdn, 'land_acquisition', frm._land_acquisition);
}
}
}
});
6 changes: 2 additions & 4 deletions landms/sales_order_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import frappe
from frappe.utils import add_days, cint, cstr, flt, getdate, today

from landms.landms.doctype.plot_master.plot_master import PLOT_TYPE_TO_ITEM
from landms.landms.doctype.plot_master.plot_master import get_plot_item_code
from landms.tcb import (
_get_tcb_settings,
create_or_get_registry,
Expand Down Expand Up @@ -202,9 +202,7 @@ def ensure_plot_sales_invoice_for_sales_order(


def build_sales_order_item_row(plot, warehouse, delivery_date):
item_code = PLOT_TYPE_TO_ITEM.get(plot.plot_type)
if not item_code:
frappe.throw(f"No item is mapped for plot type {plot.plot_type}.")
item_code = get_plot_item_code(plot.plot_type)

item = frappe.db.get_value(
"Item",
Expand Down